mirror of https://github.com/arendst/Tasmota.git
Merge branch 'development' into prerelease-14.4.1
This commit is contained in:
commit
4dfac9b414
18
CHANGELOG.md
18
CHANGELOG.md
|
@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
## [Released]
|
## [Released]
|
||||||
|
|
||||||
|
## [14.4.1] 20241215
|
||||||
|
- Release Rudolph
|
||||||
|
|
||||||
|
## [14.4.0.1] 20241215
|
||||||
|
### Added
|
||||||
|
- MCP23XXX_DRV control register IOCON in template (#22622)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Berry make Leds animate calls reentrant (#22643)
|
||||||
|
- SSL clean up remnants of old fingerprint algorithm (#22645)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- ESP32 rules operation priority regression from v13.3.0.4 (#22636)
|
||||||
|
- GUI display power button regression from v14.3.0.5 (#15788)
|
||||||
|
- MCP23xxx, PCF8574 and Shift595 power control when a display is configured regression from v14.3.0.7
|
||||||
|
|
||||||
## [14.4.0] 20241211
|
## [14.4.0] 20241211
|
||||||
- Release Rudolph
|
- Release Rudolph
|
||||||
|
|
||||||
|
@ -47,8 +63,6 @@ All notable changes to this project will be documented in this file.
|
||||||
- Add GUI submenu headers and refresh configuration button text (#22592)
|
- Add GUI submenu headers and refresh configuration button text (#22592)
|
||||||
- ESP8266 Device Group exception due to lack of stack space (#22271)
|
- ESP8266 Device Group exception due to lack of stack space (#22271)
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
## [14.3.0.6] 20241116
|
## [14.3.0.6] 20241116
|
||||||
### Added
|
### Added
|
||||||
- Add command ``WebColor20`` to control color of Button when Off
|
- Add command ``WebColor20`` to control color of Button when Off
|
||||||
|
|
|
@ -75,7 +75,7 @@ Latest released binaries can be downloaded from
|
||||||
- http://ota.tasmota.com/tasmota/release
|
- http://ota.tasmota.com/tasmota/release
|
||||||
|
|
||||||
Historical binaries can be downloaded from
|
Historical binaries can be downloaded from
|
||||||
- http://ota.tasmota.com/tasmota/release-14.4.0
|
- http://ota.tasmota.com/tasmota/release-14.4.1
|
||||||
|
|
||||||
The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmota.com/tasmota/release/tasmota.bin.gz``
|
The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmota.com/tasmota/release/tasmota.bin.gz``
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ Latest released binaries can be downloaded from
|
||||||
- https://ota.tasmota.com/tasmota32/release
|
- https://ota.tasmota.com/tasmota32/release
|
||||||
|
|
||||||
Historical binaries can be downloaded from
|
Historical binaries can be downloaded from
|
||||||
- https://ota.tasmota.com/tasmota32/release-14.4.0
|
- https://ota.tasmota.com/tasmota32/release-14.4.1
|
||||||
|
|
||||||
The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasmota.com/tasmota32/release/tasmota32.bin``
|
The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasmota.com/tasmota32/release/tasmota32.bin``
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm
|
||||||
|
|
||||||
[Complete list](BUILDS.md) of available feature and sensors.
|
[Complete list](BUILDS.md) of available feature and sensors.
|
||||||
|
|
||||||
## Changelog v14.4.0 Rudolph
|
## Changelog v14.4.1 Rudolph
|
||||||
### Added
|
### Added
|
||||||
- Command `WebColor20` to control color of Button when Off
|
- Command `WebColor20` to control color of Button when Off
|
||||||
- Command `SetOption161 1` to disable display of state text [#22515](https://github.com/arendst/Tasmota/issues/22515)
|
- Command `SetOption161 1` to disable display of state text [#22515](https://github.com/arendst/Tasmota/issues/22515)
|
||||||
|
@ -139,6 +139,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm
|
||||||
- Mitsubishi Electric HVAC Outdoor Temperature for MiElHVAC [#22345](https://github.com/arendst/Tasmota/issues/22345)
|
- Mitsubishi Electric HVAC Outdoor Temperature for MiElHVAC [#22345](https://github.com/arendst/Tasmota/issues/22345)
|
||||||
- Mitsubishi Electric HVAC Compressor Frequency for MiElHVAC [#22347](https://github.com/arendst/Tasmota/issues/22347)
|
- Mitsubishi Electric HVAC Compressor Frequency for MiElHVAC [#22347](https://github.com/arendst/Tasmota/issues/22347)
|
||||||
- Mitsubishi Electric HVAC Auto Clear Remote Temp for MiElHVAC [#22370](https://github.com/arendst/Tasmota/issues/22370)
|
- Mitsubishi Electric HVAC Auto Clear Remote Temp for MiElHVAC [#22370](https://github.com/arendst/Tasmota/issues/22370)
|
||||||
|
- MCP23XXX_DRV control register IOCON in template [#22622](https://github.com/arendst/Tasmota/issues/22622)
|
||||||
- SolaxX1 Meter mode [#22330](https://github.com/arendst/Tasmota/issues/22330)
|
- SolaxX1 Meter mode [#22330](https://github.com/arendst/Tasmota/issues/22330)
|
||||||
- Show Active Power Total with any multi-phase energy monitoring [#22579](https://github.com/arendst/Tasmota/issues/22579)
|
- Show Active Power Total with any multi-phase energy monitoring [#22579](https://github.com/arendst/Tasmota/issues/22579)
|
||||||
- ESP32 support for WPA2/3 Enterprise conditional in core v3.1.0.241206 [#22600](https://github.com/arendst/Tasmota/issues/22600)
|
- ESP32 support for WPA2/3 Enterprise conditional in core v3.1.0.241206 [#22600](https://github.com/arendst/Tasmota/issues/22600)
|
||||||
|
@ -172,8 +173,10 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm
|
||||||
- DALI set Tasmota light control as default
|
- DALI set Tasmota light control as default
|
||||||
- Shutter optimized behavior to publish shutter data with sensor request [#22353](https://github.com/arendst/Tasmota/issues/22353)
|
- Shutter optimized behavior to publish shutter data with sensor request [#22353](https://github.com/arendst/Tasmota/issues/22353)
|
||||||
- Prevent active BLE operations with unencrypted MI-format beacons [#22453](https://github.com/arendst/Tasmota/issues/22453)
|
- Prevent active BLE operations with unencrypted MI-format beacons [#22453](https://github.com/arendst/Tasmota/issues/22453)
|
||||||
|
- SSL clean up remnants of old fingerprint algorithm [#22645](https://github.com/arendst/Tasmota/issues/22645)
|
||||||
- ESP32 max number of supported switches/buttons/relays from 28 to 32
|
- ESP32 max number of supported switches/buttons/relays from 28 to 32
|
||||||
- ESP32 max number of interlocks from 14 to 16
|
- ESP32 max number of interlocks from 14 to 16
|
||||||
|
- Berry make Leds animate calls reentrant [#22643](https://github.com/arendst/Tasmota/issues/22643)
|
||||||
- HASPmota support for page delete and object updates [#22311](https://github.com/arendst/Tasmota/issues/22311)
|
- HASPmota support for page delete and object updates [#22311](https://github.com/arendst/Tasmota/issues/22311)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
@ -188,11 +191,12 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm
|
||||||
- Mitsubishi Electric HVAC Standby Stage for MiElHVAC [#22430](https://github.com/arendst/Tasmota/issues/22430)
|
- Mitsubishi Electric HVAC Standby Stage for MiElHVAC [#22430](https://github.com/arendst/Tasmota/issues/22430)
|
||||||
- EQ3 TRV firmware version 1.46 fails if the default true is used in subscribe on the notify characteristic [#22328](https://github.com/arendst/Tasmota/issues/22328)
|
- EQ3 TRV firmware version 1.46 fails if the default true is used in subscribe on the notify characteristic [#22328](https://github.com/arendst/Tasmota/issues/22328)
|
||||||
- Ethernet on -DFRAMEWORK_ARDUINO_ITEAD framework regression from v14.3.0 [#22367](https://github.com/arendst/Tasmota/issues/22367)
|
- Ethernet on -DFRAMEWORK_ARDUINO_ITEAD framework regression from v14.3.0 [#22367](https://github.com/arendst/Tasmota/issues/22367)
|
||||||
- ESP8266 Device Group exception due to lack of stack space (#22271)[#22271](https://github.com/arendst/Tasmota/issues/22271)
|
- MCP23xxx, PCF8574 and Shift595 power control when a display is configured regression from v14.3.0.7
|
||||||
|
- GUI display power button regression from v14.3.0.5 [#15788](https://github.com/arendst/Tasmota/issues/15788)
|
||||||
|
- ESP8266 Device Group exception due to lack of stack space [#22271](https://github.com/arendst/Tasmota/issues/22271)
|
||||||
|
- ESP32 rules operation priority regression from v13.3.0.4 [#22636](https://github.com/arendst/Tasmota/issues/22636)
|
||||||
- ESP32 Upgrade by file upload response based on file size [#22500](https://github.com/arendst/Tasmota/issues/22500)
|
- ESP32 Upgrade by file upload response based on file size [#22500](https://github.com/arendst/Tasmota/issues/22500)
|
||||||
- ESP32 Arduino Core IPv6 zones used by Matter [#22378](https://github.com/arendst/Tasmota/issues/22378)
|
- ESP32 Arduino Core IPv6 zones used by Matter [#22378](https://github.com/arendst/Tasmota/issues/22378)
|
||||||
- ESP32, ESP32-S2 and ESP32-S3 re-enable touch buttons [#22446](https://github.com/arendst/Tasmota/issues/22446)
|
- ESP32, ESP32-S2 and ESP32-S3 re-enable touch buttons [#22446](https://github.com/arendst/Tasmota/issues/22446)
|
||||||
- ESP32-S3 UART output mode for Tx [#22426](https://github.com/arendst/Tasmota/issues/22426)
|
- ESP32-S3 UART output mode for Tx [#22426](https://github.com/arendst/Tasmota/issues/22426)
|
||||||
- Matter provisioning with matter.js controller [#22470](https://github.com/arendst/Tasmota/issues/22470)
|
- Matter provisioning with matter.js controller [#22470](https://github.com/arendst/Tasmota/issues/22470)
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
|
@ -767,59 +767,47 @@ extern "C" {
|
||||||
xc->done_cert = true; // first cert already processed
|
xc->done_cert = true; // first cert already processed
|
||||||
}
|
}
|
||||||
|
|
||||||
// **** Start patch Castellucci
|
// Add a data element with a u32be length prefix to the sha1 state.
|
||||||
/*
|
static void sha1_update_len(br_sha1_context *shactx, const void *msg, uint32_t len) {
|
||||||
static void pubkeyfingerprint_pubkey_fingerprint(br_sha1_context *shactx, br_rsa_public_key rsakey) {
|
|
||||||
br_sha1_init(shactx);
|
|
||||||
br_sha1_update(shactx, "ssh-rsa", 7); // tag
|
|
||||||
br_sha1_update(shactx, rsakey.e, rsakey.elen); // exponent
|
|
||||||
br_sha1_update(shactx, rsakey.n, rsakey.nlen); // modulus
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
// If `compat` id false, adds a u32be length prefixed value to the sha1 state.
|
|
||||||
// If `compat` is true, the length will be omitted for compatibility with
|
|
||||||
// data from older versions of Tasmota.
|
|
||||||
static void sha1_update_len(br_sha1_context *shactx, const void *msg, uint32_t len, bool compat) {
|
|
||||||
uint8_t buf[] = {0, 0, 0, 0};
|
uint8_t buf[] = {0, 0, 0, 0};
|
||||||
|
|
||||||
if (!compat) {
|
buf[0] = (len >> 24) & 0xff;
|
||||||
buf[0] = (len >> 24) & 0xff;
|
buf[1] = (len >> 16) & 0xff;
|
||||||
buf[1] = (len >> 16) & 0xff;
|
buf[2] = (len >> 8) & 0xff;
|
||||||
buf[2] = (len >> 8) & 0xff;
|
buf[3] = (len >> 0) & 0xff;
|
||||||
buf[3] = (len >> 0) & 0xff;
|
br_sha1_update(shactx, buf, 4); // length
|
||||||
br_sha1_update(shactx, buf, 4); // length
|
|
||||||
}
|
|
||||||
br_sha1_update(shactx, msg, len); // message
|
br_sha1_update(shactx, msg, len); // message
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the received fingerprint based on the certificate's public key.
|
// Calculate the received fingerprint based on the certificate's public key.
|
||||||
// If `compat` is true, an insecure version of the fingerprint will be
|
// The public exponent and modulus are length prefixed to avoid security
|
||||||
// calcualted for compatibility with older versions of Tasmota. Normally,
|
// vulnerabilities related to ambiguous serialization. Without this, an
|
||||||
// `compat` should be false.
|
// attacker can generate alternative public keys which result in the same
|
||||||
static void pubkeyfingerprint_pubkey_fingerprint(br_x509_pubkeyfingerprint_context *xc, bool compat) {
|
// fingerprint, but are trivial to crack. This works because RSA keys can be
|
||||||
|
// created with more than two primes, and most numbers, even large ones, can
|
||||||
|
// be easily factored.
|
||||||
|
static void pubkeyfingerprint_pubkey_fingerprint(br_x509_pubkeyfingerprint_context *xc) {
|
||||||
br_rsa_public_key rsakey = xc->ctx.pkey.key.rsa;
|
br_rsa_public_key rsakey = xc->ctx.pkey.key.rsa;
|
||||||
|
|
||||||
br_sha1_context shactx;
|
br_sha1_context shactx;
|
||||||
|
|
||||||
br_sha1_init(&shactx);
|
br_sha1_init(&shactx);
|
||||||
|
|
||||||
sha1_update_len(&shactx, "ssh-rsa", 7, compat); // tag
|
// The tag string doesn't really matter, but it should differ depending on
|
||||||
sha1_update_len(&shactx, rsakey.e, rsakey.elen, compat); // exponent
|
// key type. Since we only support RSA for now, it's a fixed string.
|
||||||
sha1_update_len(&shactx, rsakey.n, rsakey.nlen, compat); // modulus
|
sha1_update_len(&shactx, "ssh-rsa", 7); // tag
|
||||||
|
sha1_update_len(&shactx, rsakey.e, rsakey.elen); // exponent
|
||||||
|
sha1_update_len(&shactx, rsakey.n, rsakey.nlen); // modulus
|
||||||
|
|
||||||
br_sha1_out(&shactx, xc->pubkey_recv_fingerprint); // copy to fingerprint
|
br_sha1_out(&shactx, xc->pubkey_recv_fingerprint); // copy to fingerprint
|
||||||
}
|
}
|
||||||
// **** End patch Castellucci
|
|
||||||
|
|
||||||
// Callback when complete chain has been parsed.
|
// Callback when complete chain has been parsed.
|
||||||
// Return 0 on validation success, !0 on validation error
|
// Return 0 on validation success, !0 on validation error
|
||||||
static unsigned pubkeyfingerprint_end_chain(const br_x509_class **ctx) {
|
static unsigned pubkeyfingerprint_end_chain(const br_x509_class **ctx) {
|
||||||
br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx;
|
br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx;
|
||||||
// set fingerprint status byte to zero
|
pubkeyfingerprint_pubkey_fingerprint(xc);
|
||||||
// FIXME: find a better way to pass this information
|
|
||||||
xc->pubkey_recv_fingerprint[20] = 0;
|
|
||||||
// Try matching using the the new fingerprint algorithm
|
|
||||||
pubkeyfingerprint_pubkey_fingerprint(xc, false);
|
|
||||||
if (!xc->fingerprint_all) {
|
if (!xc->fingerprint_all) {
|
||||||
if (0 == memcmp_P(xc->pubkey_recv_fingerprint, xc->fingerprint1, 20)) {
|
if (0 == memcmp_P(xc->pubkey_recv_fingerprint, xc->fingerprint1, 20)) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -832,7 +820,6 @@ extern "C" {
|
||||||
// Default (no validation at all) or no errors in prior checks = success.
|
// Default (no validation at all) or no errors in prior checks = success.
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// **** End patch Castellucci
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the public key from the validator (set by x509_minimal)
|
// Return the public key from the validator (set by x509_minimal)
|
||||||
|
|
|
@ -152,12 +152,7 @@ class WiFiClientSecure_light : public WiFiClient {
|
||||||
bool _insecure; // force fingerprint
|
bool _insecure; // force fingerprint
|
||||||
const uint8_t *_fingerprint1; // fingerprint1 to be checked against
|
const uint8_t *_fingerprint1; // fingerprint1 to be checked against
|
||||||
const uint8_t *_fingerprint2; // fingerprint2 to be checked against
|
const uint8_t *_fingerprint2; // fingerprint2 to be checked against
|
||||||
// **** Start patch Castellucci
|
|
||||||
/*
|
|
||||||
uint8_t _recv_fingerprint[20]; // fingerprint received
|
uint8_t _recv_fingerprint[20]; // fingerprint received
|
||||||
*/
|
|
||||||
uint8_t _recv_fingerprint[21]; // fingerprint received
|
|
||||||
// **** End patch Castellucci
|
|
||||||
|
|
||||||
unsigned char *_recvapp_buf;
|
unsigned char *_recvapp_buf;
|
||||||
size_t _recvapp_len;
|
size_t _recvapp_len;
|
||||||
|
|
|
@ -110,6 +110,14 @@ class Animate_core
|
||||||
self.strip.clear()
|
self.strip.clear()
|
||||||
end
|
end
|
||||||
def start()
|
def start()
|
||||||
|
# check if the strip had a different animate object, stop it
|
||||||
|
var prev_animate = self.strip.get_animate()
|
||||||
|
import introspect
|
||||||
|
if (prev_animate != nil) && (type(prev_animate) == 'instance') && (prev_animate != self)
|
||||||
|
prev_animate.stop()
|
||||||
|
end
|
||||||
|
self.strip.set_animate(self)
|
||||||
|
|
||||||
self.running = true
|
self.running = true
|
||||||
var animators = self.animators
|
var animators = self.animators
|
||||||
var idx = 0
|
var idx = 0
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
* Generated code, don't edit *
|
* Generated code, don't edit *
|
||||||
\********************************************************************/
|
\********************************************************************/
|
||||||
#include "be_constobj.h"
|
#include "be_constobj.h"
|
||||||
// compact class 'Animate_core' ktab size: 48, total: 98 (saved 400 bytes)
|
// compact class 'Animate_core' ktab size: 52, total: 104 (saved 416 bytes)
|
||||||
static const bvalue be_ktab_class_Animate_core[48] = {
|
static const bvalue be_ktab_class_Animate_core[52] = {
|
||||||
/* K0 */ be_nested_str_weak(stop),
|
/* K0 */ be_nested_str_weak(stop),
|
||||||
/* K1 */ be_nested_str_weak(strip),
|
/* K1 */ be_nested_str_weak(strip),
|
||||||
/* K2 */ be_nested_str_weak(clear),
|
/* K2 */ be_nested_str_weak(clear),
|
||||||
|
@ -51,8 +51,12 @@ static const bvalue be_ktab_class_Animate_core[48] = {
|
||||||
/* K43 */ be_nested_str_weak(set_cb),
|
/* K43 */ be_nested_str_weak(set_cb),
|
||||||
/* K44 */ be_nested_str_weak(set_back_color),
|
/* K44 */ be_nested_str_weak(set_back_color),
|
||||||
/* K45 */ be_nested_str_weak(add_animator),
|
/* K45 */ be_nested_str_weak(add_animator),
|
||||||
/* K46 */ be_nested_str_weak(start),
|
/* K46 */ be_nested_str_weak(get_animate),
|
||||||
/* K47 */ be_nested_str_weak(add_fast_loop),
|
/* K47 */ be_nested_str_weak(introspect),
|
||||||
|
/* K48 */ be_nested_str_weak(instance),
|
||||||
|
/* K49 */ be_nested_str_weak(set_animate),
|
||||||
|
/* K50 */ be_nested_str_weak(start),
|
||||||
|
/* K51 */ be_nested_str_weak(add_fast_loop),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -716,7 +720,7 @@ be_local_closure(class_Animate_core_remove, /* name */
|
||||||
********************************************************************/
|
********************************************************************/
|
||||||
be_local_closure(class_Animate_core_start, /* name */
|
be_local_closure(class_Animate_core_start, /* name */
|
||||||
be_nested_proto(
|
be_nested_proto(
|
||||||
6, /* nstack */
|
8, /* nstack */
|
||||||
1, /* argc */
|
1, /* argc */
|
||||||
10, /* varg */
|
10, /* varg */
|
||||||
0, /* has upvals */
|
0, /* has upvals */
|
||||||
|
@ -727,27 +731,47 @@ be_local_closure(class_Animate_core_start, /* name */
|
||||||
&be_ktab_class_Animate_core, /* shared constants */
|
&be_ktab_class_Animate_core, /* shared constants */
|
||||||
be_str_weak(start),
|
be_str_weak(start),
|
||||||
&be_const_str_solidified,
|
&be_const_str_solidified,
|
||||||
( &(const binstruction[20]) { /* code */
|
( &(const binstruction[40]) { /* code */
|
||||||
0x50040200, // 0000 LDBOOL R1 1 0
|
0x88040101, // 0000 GETMBR R1 R0 K1
|
||||||
0x90021601, // 0001 SETMBR R0 K11 R1
|
0x8C04032E, // 0001 GETMET R1 R1 K46
|
||||||
0x8804010C, // 0002 GETMBR R1 R0 K12
|
0x7C040200, // 0002 CALL R1 1
|
||||||
0x58080007, // 0003 LDCONST R2 K7
|
0xA40A5E00, // 0003 IMPORT R2 K47
|
||||||
0x600C000C, // 0004 GETGBL R3 G12
|
0x4C0C0000, // 0004 LDNIL R3
|
||||||
0x5C100200, // 0005 MOVE R4 R1
|
0x200C0203, // 0005 NE R3 R1 R3
|
||||||
0x7C0C0200, // 0006 CALL R3 1
|
0x780E0008, // 0006 JMPF R3 #0010
|
||||||
0x140C0403, // 0007 LT R3 R2 R3
|
0x600C0004, // 0007 GETGBL R3 G4
|
||||||
0x780E0004, // 0008 JMPF R3 #000E
|
0x5C100200, // 0008 MOVE R4 R1
|
||||||
0x940C0202, // 0009 GETIDX R3 R1 R2
|
0x7C0C0200, // 0009 CALL R3 1
|
||||||
0x8C0C072E, // 000A GETMET R3 R3 K46
|
0x1C0C0730, // 000A EQ R3 R3 K48
|
||||||
0x7C0C0200, // 000B CALL R3 1
|
0x780E0003, // 000B JMPF R3 #0010
|
||||||
0x0008050D, // 000C ADD R2 R2 K13
|
0x200C0200, // 000C NE R3 R1 R0
|
||||||
0x7001FFF5, // 000D JMP #0004
|
0x780E0001, // 000D JMPF R3 #0010
|
||||||
0x90022707, // 000E SETMBR R0 K19 K7
|
0x8C0C0300, // 000E GETMET R3 R1 K0
|
||||||
0xB80E0800, // 000F GETNGBL R3 K4
|
0x7C0C0200, // 000F CALL R3 1
|
||||||
0x8C0C072F, // 0010 GETMET R3 R3 K47
|
0x880C0101, // 0010 GETMBR R3 R0 K1
|
||||||
0x8814010F, // 0011 GETMBR R5 R0 K15
|
0x8C0C0731, // 0011 GETMET R3 R3 K49
|
||||||
0x7C0C0400, // 0012 CALL R3 2
|
0x5C140000, // 0012 MOVE R5 R0
|
||||||
0x80000000, // 0013 RET 0
|
0x7C0C0400, // 0013 CALL R3 2
|
||||||
|
0x500C0200, // 0014 LDBOOL R3 1 0
|
||||||
|
0x90021603, // 0015 SETMBR R0 K11 R3
|
||||||
|
0x880C010C, // 0016 GETMBR R3 R0 K12
|
||||||
|
0x58100007, // 0017 LDCONST R4 K7
|
||||||
|
0x6014000C, // 0018 GETGBL R5 G12
|
||||||
|
0x5C180600, // 0019 MOVE R6 R3
|
||||||
|
0x7C140200, // 001A CALL R5 1
|
||||||
|
0x14140805, // 001B LT R5 R4 R5
|
||||||
|
0x78160004, // 001C JMPF R5 #0022
|
||||||
|
0x94140604, // 001D GETIDX R5 R3 R4
|
||||||
|
0x8C140B32, // 001E GETMET R5 R5 K50
|
||||||
|
0x7C140200, // 001F CALL R5 1
|
||||||
|
0x0010090D, // 0020 ADD R4 R4 K13
|
||||||
|
0x7001FFF5, // 0021 JMP #0018
|
||||||
|
0x90022707, // 0022 SETMBR R0 K19 K7
|
||||||
|
0xB8160800, // 0023 GETNGBL R5 K4
|
||||||
|
0x8C140B33, // 0024 GETMET R5 R5 K51
|
||||||
|
0x881C010F, // 0025 GETMBR R7 R0 K15
|
||||||
|
0x7C140400, // 0026 CALL R5 2
|
||||||
|
0x80000000, // 0027 RET 0
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
|
@ -16,7 +16,6 @@ extern int be_leds_apply_bri_gamma(bvm *vm);
|
||||||
/* @const_object_info_begin
|
/* @const_object_info_begin
|
||||||
class be_class_Leds_ntv (scope: global, name: Leds_ntv, strings: weak) {
|
class be_class_Leds_ntv (scope: global, name: Leds_ntv, strings: weak) {
|
||||||
_p, var
|
_p, var
|
||||||
_t, var
|
|
||||||
|
|
||||||
WS2812_GRB, int(ws2812_grb)
|
WS2812_GRB, int(ws2812_grb)
|
||||||
SK6812_GRBW, int(sk6812_grbw)
|
SK6812_GRBW, int(sk6812_grbw)
|
||||||
|
|
|
@ -28,6 +28,7 @@ class Leds : Leds_ntv
|
||||||
var gamma # if true, apply gamma (true is default)
|
var gamma # if true, apply gamma (true is default)
|
||||||
var leds # number of leds
|
var leds # number of leds
|
||||||
var bri # implicit brightness for this led strip (0..255, default is 50% = 127)
|
var bri # implicit brightness for this led strip (0..255, default is 50% = 127)
|
||||||
|
var animate # attached animate object or nil - this allows to stop any existing animation for this strip if we add a new animate
|
||||||
# leds:int = number of leds of the strip
|
# leds:int = number of leds of the strip
|
||||||
# gpio:int (optional) = GPIO for NeoPixel. If not specified, takes the WS2812 gpio
|
# gpio:int (optional) = GPIO for NeoPixel. If not specified, takes the WS2812 gpio
|
||||||
# typ:int (optional) = Type of LED, defaults to WS2812 RGB
|
# typ:int (optional) = Type of LED, defaults to WS2812 RGB
|
||||||
|
@ -35,25 +36,43 @@ class Leds : Leds_ntv
|
||||||
def init(leds, gpio_phy, typ, hardware)
|
def init(leds, gpio_phy, typ, hardware)
|
||||||
import gpio
|
import gpio
|
||||||
self.gamma = true # gamma is enabled by default, it should be disabled explicitly if needed
|
self.gamma = true # gamma is enabled by default, it should be disabled explicitly if needed
|
||||||
if (gpio_phy == nil) || (gpio_phy == gpio.pin(gpio.WS2812, 0))
|
if (leds == nil ) || (gpio_phy == nil) || (gpio_phy == gpio.pin(gpio.WS2812, 0))
|
||||||
# use native driver
|
# use native driver
|
||||||
self.ctor() # no parameters
|
self.ctor() # no parameters
|
||||||
|
# in such case, `self._p` is equal to `0`
|
||||||
self.leds = self.pixel_count()
|
self.leds = self.pixel_count()
|
||||||
import light
|
import light
|
||||||
self.bri = light.get()['bri']
|
self.bri = light.get()['bri']
|
||||||
else
|
else
|
||||||
# use pure Berry driver
|
# use pure Berry driver
|
||||||
self.leds = int(leds)
|
leds = int(leds)
|
||||||
|
self.leds = leds
|
||||||
self.bri = 127 # 50% brightness by default
|
self.bri = 127 # 50% brightness by default
|
||||||
|
|
||||||
# initialize the structure
|
# initialize the structure
|
||||||
self.ctor(self.leds, gpio_phy, typ, hardware)
|
# check if already in global `_lhw`
|
||||||
|
if !global.contains('_lhw')
|
||||||
|
global._lhw = {}
|
||||||
|
end
|
||||||
|
if global._lhw.find(leds) != nil
|
||||||
|
# an object already exists, attach it
|
||||||
|
var prov_led = global._lhw.find(leds) # already provisioned leds instance
|
||||||
|
if self.leds != prov_led.leds
|
||||||
|
raise "value_error", f"number of leds do not match with previous instanciation {self.leds} vs {prov_led.leds}"
|
||||||
|
end
|
||||||
|
self._p = prov_led._p
|
||||||
|
self.animate = prov_led.animate
|
||||||
|
global._lhw[leds] = self # put the most recent as current
|
||||||
|
else
|
||||||
|
self.ctor(leds, gpio_phy, typ, hardware)
|
||||||
|
global._lhw[leds] = self
|
||||||
|
# call begin
|
||||||
|
self.begin()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if self._p == nil raise "internal_error", "couldn't not initialize noepixelbus" end
|
if self._p == nil raise "internal_error", "couldn't not initialize noepixelbus" end
|
||||||
|
|
||||||
# call begin
|
|
||||||
self.begin()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def clear()
|
def clear()
|
||||||
|
@ -71,6 +90,13 @@ class Leds : Leds_ntv
|
||||||
return self.bri
|
return self.bri
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_animate(animate)
|
||||||
|
self.animate = animate
|
||||||
|
end
|
||||||
|
def get_animate()
|
||||||
|
return self.animate
|
||||||
|
end
|
||||||
|
|
||||||
def ctor(leds, gpio_phy, typ, hardware)
|
def ctor(leds, gpio_phy, typ, hardware)
|
||||||
if gpio_phy == nil
|
if gpio_phy == nil
|
||||||
self.call_native(0) # native driver
|
self.call_native(0) # native driver
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -53,7 +53,7 @@ def addEntryToModtab(source):
|
||||||
is_module = False
|
is_module = False
|
||||||
|
|
||||||
|
|
||||||
pattern = (r'''(?<=module\()[^"].*''') # module??
|
pattern = (r'''(?<=module\([\"\']).*[\"\']''') # module??
|
||||||
result = re.findall(pattern,code)
|
result = re.findall(pattern,code)
|
||||||
if len(result) > 0:
|
if len(result) > 0:
|
||||||
class_name = result[0].replace("'","").replace('"','').replace(")","")
|
class_name = result[0].replace("'","").replace('"','').replace(")","")
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
:H,ST7701,480,480,16,RGB,40,39,38,41,-1,5,45,48,47,21,14,13,12,11,10,9,46,3,8,18,17,6
|
||||||
|
:V,1,10,8,50,1,10,8,20,0
|
||||||
|
:S,2,1,1,0,40,20
|
||||||
|
:IS,2,1,42,-1
|
||||||
|
11,80
|
||||||
|
FF,5,77,01,00,00,10
|
||||||
|
C0,2,3B,00
|
||||||
|
C1,2,0D,02
|
||||||
|
C2,2,21,08
|
||||||
|
CD,1,08
|
||||||
|
B0,10,00,11,18,0E,11,06,07,08,07,22,04,12,0F,AA,31,18
|
||||||
|
B1,10,00,11,19,0E,12,07,08,08,08,22,04,11,11,A9,32,18
|
||||||
|
FF,5,77,01,00,00,11
|
||||||
|
B0,1,60
|
||||||
|
B1,1,30
|
||||||
|
B2,1,87
|
||||||
|
B3,1,80
|
||||||
|
B5,1,49
|
||||||
|
B7,1,85
|
||||||
|
B8,1,21
|
||||||
|
C1,1,78
|
||||||
|
C2,1,78,20
|
||||||
|
E0,3,00,1B,02
|
||||||
|
E1,B,08,A0,00,00,07,A0,00,00,00,44,44
|
||||||
|
E2,C,11,11,44,44,ED,A0,00,00,EC,A0,00,00
|
||||||
|
E3,4,00,00,11,11
|
||||||
|
E4,2,44,44
|
||||||
|
E5,10,0A,E9,D8,A0,0C,EB,D8,A0,0E,ED,D8,A0,10,EF,D8,A0
|
||||||
|
E6,4,00,00,11,11
|
||||||
|
E7,2,44,44
|
||||||
|
E8,10,09,E8,D8,A0,0B,EA,D8,A0,0D,EC,D8,A0,0F,EE,D8,A0
|
||||||
|
EB,7,02,00,E4,E4,88,00,40
|
||||||
|
EC,2,3C,00
|
||||||
|
ED,10,AB,89,76,54,02,FF,FF,FF,FF,FF,FF,20,45,67,98,BA
|
||||||
|
FF,5,77,01,00,00,00
|
||||||
|
36,1,04
|
||||||
|
3A,1,66
|
||||||
|
21,80
|
||||||
|
29,0
|
||||||
|
:B,100,02
|
||||||
|
:UTI,GT911,I1,5d,-1,-1
|
||||||
|
RDWM 8140 4
|
||||||
|
MV 0 1
|
||||||
|
CPR 39
|
||||||
|
RTF
|
||||||
|
MV 1 1
|
||||||
|
CPR 31
|
||||||
|
RTF
|
||||||
|
MV 2 1
|
||||||
|
CPR 31
|
||||||
|
RTF
|
||||||
|
RT
|
||||||
|
:UTT
|
||||||
|
RDW 814E
|
||||||
|
MV 0 1
|
||||||
|
AND 80
|
||||||
|
CPR 80
|
||||||
|
RTF
|
||||||
|
RDWM 8150 8
|
||||||
|
WRW 814E 00
|
||||||
|
RT
|
||||||
|
:UTX
|
||||||
|
MV 0 3
|
||||||
|
RT
|
||||||
|
:UTY
|
||||||
|
MV 2 3
|
||||||
|
RT
|
||||||
|
#
|
|
@ -968,8 +968,8 @@ const char HTTP_SNS_F_VOLTAGE[] PROGMEM = "{s}%s " D_VOLTAGE "{
|
||||||
const char HTTP_SNS_F_CURRENT[] PROGMEM = "{s}%s " D_CURRENT "{m}%*_f " D_UNIT_AMPERE "{e}";
|
const char HTTP_SNS_F_CURRENT[] PROGMEM = "{s}%s " D_CURRENT "{m}%*_f " D_UNIT_AMPERE "{e}";
|
||||||
const char HTTP_SNS_F_CURRENT_MA[] PROGMEM = "{s}%s " D_CURRENT "{m}%*_f " D_UNIT_MILLIAMPERE "{e}";
|
const char HTTP_SNS_F_CURRENT_MA[] PROGMEM = "{s}%s " D_CURRENT "{m}%*_f " D_UNIT_MILLIAMPERE "{e}";
|
||||||
const char HTTP_SNS_F_DISTANCE_CM[] PROGMEM = "{s}%s " D_DISTANCE "{m}%1_f " D_UNIT_CENTIMETER "{e}";
|
const char HTTP_SNS_F_DISTANCE_CM[] PROGMEM = "{s}%s " D_DISTANCE "{m}%1_f " D_UNIT_CENTIMETER "{e}";
|
||||||
const char HTTP_SNS_F_NOX[] PROGMEM = "{s}%s " D_NOX "{m}%*_f " "{e}";
|
const char HTTP_SNS_F_NOX[] PROGMEM = "{s}%s " D_NOX "{m}%*_f" "{e}";
|
||||||
const char HTTP_SNS_F_VOC[] PROGMEM = "{s}%s " D_VOC "{m}%*_f " "{e}";
|
const char HTTP_SNS_F_VOC[] PROGMEM = "{s}%s " D_VOC "{m}%*_f" "{e}";
|
||||||
const char HTTP_SNS_F_ABS_HUM[] PROGMEM = "{s}%s " D_ABSOLUTE_HUMIDITY "{m}%*_f " D_UNIT_GRAM_PER_CUBIC_METER "{e}";
|
const char HTTP_SNS_F_ABS_HUM[] PROGMEM = "{s}%s " D_ABSOLUTE_HUMIDITY "{m}%*_f " D_UNIT_GRAM_PER_CUBIC_METER "{e}";
|
||||||
|
|
||||||
const char HTTP_SNS_HUM[] PROGMEM = "{s}%s " D_HUMIDITY "{m}%s " D_UNIT_PERCENT "{e}";
|
const char HTTP_SNS_HUM[] PROGMEM = "{s}%s " D_HUMIDITY "{m}%s " D_UNIT_PERCENT "{e}";
|
||||||
|
@ -987,17 +987,17 @@ const char HTTP_SNS_MOISTURE[] PROGMEM = "{s}%s " D_MOISTURE "{
|
||||||
const char HTTP_SNS_RANGE_CHR[] PROGMEM = "{s}%s " D_RANGE "{m}%s" "{e}";
|
const char HTTP_SNS_RANGE_CHR[] PROGMEM = "{s}%s " D_RANGE "{m}%s" "{e}";
|
||||||
const char HTTP_SNS_RANGE[] PROGMEM = "{s}%s " D_RANGE "{m}%d" "{e}";
|
const char HTTP_SNS_RANGE[] PROGMEM = "{s}%s " D_RANGE "{m}%d" "{e}";
|
||||||
const char HTTP_SNS_HALL_EFFECT[] PROGMEM = "{s}%s " D_HALL_EFFECT "{m}%d" "{e}";
|
const char HTTP_SNS_HALL_EFFECT[] PROGMEM = "{s}%s " D_HALL_EFFECT "{m}%d" "{e}";
|
||||||
const char HTTP_SNS_PH[] PROGMEM = "{s}%s " D_PH "{m}%s " "{e}";
|
const char HTTP_SNS_PH[] PROGMEM = "{s}%s " D_PH "{m}%s" "{e}";
|
||||||
const char HTTP_SNS_MQ[] PROGMEM = "{s}" D_MQ"-%s" "{m}%s " D_UNIT_PARTS_PER_MILLION "{e}";
|
const char HTTP_SNS_MQ[] PROGMEM = "{s}" D_MQ "-%s" "{m}%s " D_UNIT_PARTS_PER_MILLION "{e}";
|
||||||
const char HTTP_SNS_ORP[] PROGMEM = "{s}%s " D_ORP "{m}%s " D_UNIT_MILLIVOLT "{e}";
|
const char HTTP_SNS_ORP[] PROGMEM = "{s}%s " D_ORP "{m}%s " D_UNIT_MILLIVOLT "{e}";
|
||||||
const char HTTP_SNS_EC[] PROGMEM = "{s}%s " D_EC "{m}%s " D_UNIT_MICROSIEMENS_PER_CM "{e}";
|
const char HTTP_SNS_EC[] PROGMEM = "{s}%s " D_EC "{m}%s " D_UNIT_MICROSIEMENS_PER_CM "{e}";
|
||||||
const char HTTP_SNS_O2[] PROGMEM = "{s}%s " D_O2 "{m}%s " D_UNIT_PERCENT "{e}";
|
const char HTTP_SNS_O2[] PROGMEM = "{s}%s " D_O2 "{m}%s " D_UNIT_PERCENT "{e}";
|
||||||
const char HTTP_SNS_LITERS[] PROGMEM = "{s}%s " D_VOLUME "{m}%s " D_UNIT_LITERS "{e}";
|
const char HTTP_SNS_LITERS[] PROGMEM = "{s}%s " D_VOLUME "{m}%s " D_UNIT_LITERS "{e}";
|
||||||
const char HTTP_SNS_LPM[] PROGMEM = "{s}%s " D_FLOW_RATE "{m}%s " D_UNIT_LITERS_PER_MIN "{e}";
|
const char HTTP_SNS_LPM[] PROGMEM = "{s}%s " D_FLOW_RATE "{m}%s " D_UNIT_LITERS_PER_MIN "{e}";
|
||||||
const char HTTP_SNS_DO[] PROGMEM = "{s}%s " D_DO "{m}%s " D_UNIT_MILIGRAMS_PER_LITER "{e}";
|
const char HTTP_SNS_DO[] PROGMEM = "{s}%s " D_DO "{m}%s " D_UNIT_MILIGRAMS_PER_LITER "{e}";
|
||||||
const char HTTP_SNS_COLOR_RED[] PROGMEM = "{s}%s " D_COLOR_RED "{m}%u " "{e}";
|
const char HTTP_SNS_COLOR_RED[] PROGMEM = "{s}%s " D_COLOR_RED "{m}%u" "{e}";
|
||||||
const char HTTP_SNS_COLOR_GREEN[] PROGMEM = "{s}%s " D_COLOR_GREEN "{m}%u " "{e}";
|
const char HTTP_SNS_COLOR_GREEN[] PROGMEM = "{s}%s " D_COLOR_GREEN "{m}%u" "{e}";
|
||||||
const char HTTP_SNS_COLOR_BLUE[] PROGMEM = "{s}%s " D_COLOR_BLUE "{m}%u " "{e}";
|
const char HTTP_SNS_COLOR_BLUE[] PROGMEM = "{s}%s " D_COLOR_BLUE "{m}%u" "{e}";
|
||||||
const char HTTP_SNS_MILLILITERS[] PROGMEM = "{s}%s " D_VOLUME "{m}%s " D_UNIT_MILLILITERS "{e}";
|
const char HTTP_SNS_MILLILITERS[] PROGMEM = "{s}%s " D_VOLUME "{m}%s " D_UNIT_MILLILITERS "{e}";
|
||||||
const char HTTP_SNS_GAS[] PROGMEM = "{s}%s " D_GAS "{m}%d " D_UNIT_PERCENT "LEL{e}";
|
const char HTTP_SNS_GAS[] PROGMEM = "{s}%s " D_GAS "{m}%d " D_UNIT_PERCENT "LEL{e}";
|
||||||
const char HTTP_SNS_SOC[] PROGMEM = "{s}%s " D_SOC "{m}%d " D_UNIT_PERCENT "{e}";
|
const char HTTP_SNS_SOC[] PROGMEM = "{s}%s " D_SOC "{m}%d " D_UNIT_PERCENT "{e}";
|
||||||
|
@ -1022,7 +1022,7 @@ const char HTTP_SNS_MAX_POWER[] PROGMEM = "{s}" D_MAX_POWER
|
||||||
const char HTTP_SNS_POWER_TOTAL[] PROGMEM = "{s}" D_POWERUSAGE_ACTIVE_TOTAL "{m}%s " D_UNIT_WATT "{e}";
|
const char HTTP_SNS_POWER_TOTAL[] PROGMEM = "{s}" D_POWERUSAGE_ACTIVE_TOTAL "{m}%s " D_UNIT_WATT "{e}";
|
||||||
const char HTTP_SNS_POWERUSAGE_APPARENT[] PROGMEM = "{s}" D_POWERUSAGE_APPARENT "{m}%s " D_UNIT_VA "{e}";
|
const char HTTP_SNS_POWERUSAGE_APPARENT[] PROGMEM = "{s}" D_POWERUSAGE_APPARENT "{m}%s " D_UNIT_VA "{e}";
|
||||||
const char HTTP_SNS_POWERUSAGE_REACTIVE[] PROGMEM = "{s}" D_POWERUSAGE_REACTIVE "{m}%s " D_UNIT_VAR "{e}";
|
const char HTTP_SNS_POWERUSAGE_REACTIVE[] PROGMEM = "{s}" D_POWERUSAGE_REACTIVE "{m}%s " D_UNIT_VAR "{e}";
|
||||||
const char HTTP_SNS_POWER_FACTOR[] PROGMEM = "{s}" D_POWER_FACTOR "{m}%s {e}";
|
const char HTTP_SNS_POWER_FACTOR[] PROGMEM = "{s}" D_POWER_FACTOR "{m}%s" "{e}";
|
||||||
const char HTTP_SNS_ENERGY_TODAY[] PROGMEM = "{s}" D_ENERGY_TODAY "{m}%s " D_UNIT_KILOWATTHOUR "{e}";
|
const char HTTP_SNS_ENERGY_TODAY[] PROGMEM = "{s}" D_ENERGY_TODAY "{m}%s " D_UNIT_KILOWATTHOUR "{e}";
|
||||||
const char HTTP_SNS_ENERGY_YESTERDAY[] PROGMEM = "{s}" D_ENERGY_YESTERDAY "{m}%s " D_UNIT_KILOWATTHOUR "{e}";
|
const char HTTP_SNS_ENERGY_YESTERDAY[] PROGMEM = "{s}" D_ENERGY_YESTERDAY "{m}%s " D_UNIT_KILOWATTHOUR "{e}";
|
||||||
const char HTTP_SNS_ENERGY_TOTAL[] PROGMEM = "{s}" D_ENERGY_TOTAL "{m}%s " D_UNIT_KILOWATTHOUR "{e}";
|
const char HTTP_SNS_ENERGY_TOTAL[] PROGMEM = "{s}" D_ENERGY_TOTAL "{m}%s " D_UNIT_KILOWATTHOUR "{e}";
|
||||||
|
|
|
@ -22,6 +22,6 @@
|
||||||
|
|
||||||
#define TASMOTA_SHA_SHORT // Filled by Github sed
|
#define TASMOTA_SHA_SHORT // Filled by Github sed
|
||||||
|
|
||||||
const uint32_t TASMOTA_VERSION = 0x0E040000; // 14.4.0.0
|
const uint32_t TASMOTA_VERSION = 0x0E040100; // 14.4.1.0
|
||||||
|
|
||||||
#endif // _TASMOTA_VERSION_H_
|
#endif // _TASMOTA_VERSION_H_
|
||||||
|
|
|
@ -211,7 +211,7 @@
|
||||||
#define COLOR_TIMER_TAB_TEXT "#fff" // [WebColor17] Config timer tab text color - White
|
#define COLOR_TIMER_TAB_TEXT "#fff" // [WebColor17] Config timer tab text color - White
|
||||||
#define COLOR_TIMER_TAB_BACKGROUND "#999" // [WebColor18] Config timer tab background color - Dark gray
|
#define COLOR_TIMER_TAB_BACKGROUND "#999" // [WebColor18] Config timer tab background color - Dark gray
|
||||||
#define COLOR_TITLE_TEXT "#000" // [WebColor19] Title text color - Whiteish
|
#define COLOR_TITLE_TEXT "#000" // [WebColor19] Title text color - Whiteish
|
||||||
#define COLOR_BUTTON_OFF "#08405e" // [WebColor20] Button color when off - Darkest blueish
|
#define COLOR_BUTTON_OFF "#a1d9f7" // [WebColor20] Button color when off - Light blue
|
||||||
*/
|
*/
|
||||||
// Dark theme
|
// Dark theme
|
||||||
// WebColor {"WebColor":["#eaeaea","#252525","#4f4f4f","#000","#ddd","#65c115","#1f1f1f","#ff5661","#008000","#faffff","#1fa3ec","#0e70a4","#d43535","#931f1f","#47c266","#5aaf6f","#faffff","#999","#eaeaea","#08405e"]}
|
// WebColor {"WebColor":["#eaeaea","#252525","#4f4f4f","#000","#ddd","#65c115","#1f1f1f","#ff5661","#008000","#faffff","#1fa3ec","#0e70a4","#d43535","#931f1f","#47c266","#5aaf6f","#faffff","#999","#eaeaea","#08405e"]}
|
||||||
|
@ -234,7 +234,7 @@
|
||||||
#define COLOR_TIMER_TAB_TEXT "#faffff" // [WebColor17] Config timer tab text color - Very pale (mostly white) cyan.
|
#define COLOR_TIMER_TAB_TEXT "#faffff" // [WebColor17] Config timer tab text color - Very pale (mostly white) cyan.
|
||||||
#define COLOR_TIMER_TAB_BACKGROUND "#999" // [WebColor18] Config timer tab background color - Dark gray
|
#define COLOR_TIMER_TAB_BACKGROUND "#999" // [WebColor18] Config timer tab background color - Dark gray
|
||||||
#define COLOR_TITLE_TEXT "#eaeaea" // [WebColor19] Title text color - Very light gray
|
#define COLOR_TITLE_TEXT "#eaeaea" // [WebColor19] Title text color - Very light gray
|
||||||
#define COLOR_BUTTON_OFF "#08405e" // [WebColor20] Button color when off - Darkest blueish
|
#define COLOR_BUTTON_OFF "#08405e" // [WebColor20] Button color when off - Darkest blueish
|
||||||
|
|
||||||
// -- KNX -----------------------------------------
|
// -- KNX -----------------------------------------
|
||||||
#define KNX_ENABLED false // [Knx_Enabled] Enable KNX protocol
|
#define KNX_ENABLED false // [Knx_Enabled] Enable KNX protocol
|
||||||
|
|
|
@ -484,6 +484,8 @@ ESP8266WebServer *Webserver;
|
||||||
struct WEB {
|
struct WEB {
|
||||||
String chunk_buffer = ""; // Could be max 2 * CHUNKED_BUFFER_SIZE
|
String chunk_buffer = ""; // Could be max 2 * CHUNKED_BUFFER_SIZE
|
||||||
uint32_t upload_size = 0;
|
uint32_t upload_size = 0;
|
||||||
|
uint32_t light_shutter_button_mask;
|
||||||
|
uint32_t buttons_non_light_non_shutter;
|
||||||
uint32_t slider_update_time = 0;
|
uint32_t slider_update_time = 0;
|
||||||
int slider[LST_MAX];
|
int slider[LST_MAX];
|
||||||
int8_t shutter_slider[16]; // MAX_SHUTTERS_ESP32
|
int8_t shutter_slider[16]; // MAX_SHUTTERS_ESP32
|
||||||
|
@ -1257,33 +1259,37 @@ int32_t IsShutterWebButton(uint32_t idx) {
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
void WebGetDeviceCounts(uint32_t &buttons_non_light, uint32_t &buttons_non_light_non_shutter, uint32_t &shutter_button_mask) {
|
void WebGetDeviceCounts(void) {
|
||||||
buttons_non_light = TasmotaGlobal.devices_present;
|
Web.buttons_non_light_non_shutter = TasmotaGlobal.devices_present;
|
||||||
|
Web.light_shutter_button_mask = 0; // Bitmask for each light and/or shutter button
|
||||||
|
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
// Chk for reduced toggle buttons used by lights
|
// Chk for reduced toggle buttons used by lights
|
||||||
if (TasmotaGlobal.light_type) {
|
if (TasmotaGlobal.light_type) {
|
||||||
// Find and skip light buttons (Lights are controlled by the last TasmotaGlobal.devices_present (or 2))
|
// Find and skip light buttons
|
||||||
buttons_non_light = LightDevice() -1;
|
uint32_t light_device = LightDevice();
|
||||||
|
uint32_t light_devices = LightDevices();
|
||||||
|
for (uint32_t button_idx = light_device; button_idx < (light_device + light_devices); button_idx++) {
|
||||||
|
Web.buttons_non_light_non_shutter--;
|
||||||
|
Web.light_shutter_button_mask |= (1 << (button_idx -1)); // Set button bit in bitmask
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif // USE_LIGHT
|
#endif // USE_LIGHT
|
||||||
|
|
||||||
buttons_non_light_non_shutter = buttons_non_light;
|
|
||||||
shutter_button_mask = 0; // Bitmask for each button
|
|
||||||
#ifdef USE_SHUTTER
|
#ifdef USE_SHUTTER
|
||||||
// Chk for reduced toggle buttons used by shutters
|
// Chk for reduced toggle buttons used by shutters
|
||||||
// Find and skip dedicated shutter buttons
|
if (Settings->flag3.shutter_mode) { // SetOption80 - Enable shutter support
|
||||||
if (buttons_non_light && Settings->flag3.shutter_mode) { // SetOption80 - Enable shutter support
|
// Find and skip dedicated shutter buttons
|
||||||
for (uint32_t button_idx = 1; button_idx <= buttons_non_light; button_idx++) {
|
for (uint32_t button_idx = 1; button_idx <= TasmotaGlobal.devices_present; button_idx++) {
|
||||||
if (IsShutterWebButton(button_idx) != 0) {
|
if (IsShutterWebButton(button_idx) != 0) {
|
||||||
buttons_non_light_non_shutter--;
|
Web.buttons_non_light_non_shutter--;
|
||||||
shutter_button_mask |= (1 << (button_idx -1)); // Set button bit in bitmask
|
Web.light_shutter_button_mask |= (1 << (button_idx -1)); // Set button bit in bitmask
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // USE_SHUTTER
|
#endif // USE_SHUTTER
|
||||||
|
|
||||||
// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("HTP: DP %d, BNL %d, BNLNS %d, SB %08X"), TasmotaGlobal.devices_present, buttons_non_light, buttons_non_light_non_shutter, shutter_button);
|
// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("HTP: DP %d, BNLNS %d, SB %08X"), TasmotaGlobal.devices_present, Web.buttons_non_light_non_shutter, Web.light_shutter_button_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
|
@ -1366,13 +1372,10 @@ void HandleRoot(void) {
|
||||||
#ifndef FIRMWARE_MINIMAL
|
#ifndef FIRMWARE_MINIMAL
|
||||||
|
|
||||||
if (TasmotaGlobal.devices_present) {
|
if (TasmotaGlobal.devices_present) {
|
||||||
uint32_t buttons_non_light;
|
WebGetDeviceCounts();
|
||||||
uint32_t buttons_non_light_non_shutter;
|
|
||||||
uint32_t shutter_button_mask;
|
|
||||||
WebGetDeviceCounts(buttons_non_light, buttons_non_light_non_shutter, shutter_button_mask);
|
|
||||||
uint32_t button_idx = 1;
|
|
||||||
|
|
||||||
if (buttons_non_light_non_shutter) { // Any non light AND non shutter button - Show toggle buttons
|
uint32_t button_idx = 1;
|
||||||
|
if (Web.buttons_non_light_non_shutter) { // Any non light AND non shutter button - Show toggle buttons
|
||||||
WSContentSend_P(HTTP_TABLE100); // "<table style='width:100%%'>"
|
WSContentSend_P(HTTP_TABLE100); // "<table style='width:100%%'>"
|
||||||
WSContentSend_P(PSTR("<tr>"));
|
WSContentSend_P(PSTR("<tr>"));
|
||||||
|
|
||||||
|
@ -1391,18 +1394,14 @@ void HandleRoot(void) {
|
||||||
#endif // USE_SONOFF_IFAN
|
#endif // USE_SONOFF_IFAN
|
||||||
|
|
||||||
const uint32_t max_columns = 8;
|
const uint32_t max_columns = 8;
|
||||||
uint32_t rows = buttons_non_light_non_shutter / max_columns;
|
uint32_t rows = Web.buttons_non_light_non_shutter / max_columns;
|
||||||
if (buttons_non_light_non_shutter % max_columns) { rows++; }
|
if (Web.buttons_non_light_non_shutter % max_columns) { rows++; }
|
||||||
uint32_t cols = buttons_non_light_non_shutter / rows;
|
uint32_t cols = Web.buttons_non_light_non_shutter / rows;
|
||||||
if (buttons_non_light_non_shutter % rows) { cols++; }
|
if (Web.buttons_non_light_non_shutter % rows) { cols++; }
|
||||||
|
|
||||||
uint32_t button_ptr = 0;
|
uint32_t button_ptr = 0;
|
||||||
for (button_idx = 1; button_idx <= buttons_non_light; button_idx++) {
|
for (button_idx = 1; button_idx <= TasmotaGlobal.devices_present; button_idx++) {
|
||||||
|
if (bitRead(Web.light_shutter_button_mask, button_idx -1)) { continue; } // Skip non-sequential light and/or shutter button
|
||||||
#ifdef USE_SHUTTER
|
|
||||||
if (bitRead(shutter_button_mask, button_idx -1)) { continue; } // Skip non-sequential shutter button
|
|
||||||
#endif // USE_SHUTTER
|
|
||||||
|
|
||||||
bool set_button = ((button_idx <= MAX_BUTTON_TEXT) && strlen(GetWebButton(button_idx -1)));
|
bool set_button = ((button_idx <= MAX_BUTTON_TEXT) && strlen(GetWebButton(button_idx -1)));
|
||||||
snprintf_P(stemp, sizeof(stemp), PSTR(" %d"), button_idx);
|
snprintf_P(stemp, sizeof(stemp), PSTR(" %d"), button_idx);
|
||||||
WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / cols, button_idx, button_idx,
|
WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / cols, button_idx, button_idx,
|
||||||
|
@ -1451,20 +1450,20 @@ void HandleRoot(void) {
|
||||||
WSContentSend_P(PSTR("</tr>"));
|
WSContentSend_P(PSTR("</tr>"));
|
||||||
}
|
}
|
||||||
WSContentSend_P(PSTR("</table>"));
|
WSContentSend_P(PSTR("</table>"));
|
||||||
|
|
||||||
if (1 == button_idx) { // No power/display button
|
|
||||||
button_idx = shutter_button_idx +2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif // USE_SHUTTER
|
#endif // USE_SHUTTER
|
||||||
|
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
if (TasmotaGlobal.light_type) { // Any light - Show light button and slider(s)
|
if (TasmotaGlobal.light_type) { // Any light - Show light button and slider(s)
|
||||||
|
uint32_t light_device = LightDevice();
|
||||||
|
uint32_t light_devices = LightDevices();
|
||||||
|
button_idx = light_device;
|
||||||
|
|
||||||
WSContentSend_P(HTTP_TABLE100); // "<table style='width:100%%'>"
|
WSContentSend_P(HTTP_TABLE100); // "<table style='width:100%%'>"
|
||||||
|
|
||||||
uint8_t light_subtype = TasmotaGlobal.light_type &7;
|
uint8_t light_subtype = TasmotaGlobal.light_type &7;
|
||||||
if (!Settings->flag3.pwm_multi_channels) { // SetOption68 0 - Enable multi-channels PWM instead of Color PWM
|
if (!Settings->flag3.pwm_multi_channels) { // SetOption68 0 - Enable multi-channels PWM instead of Color PWM
|
||||||
bool split_white = ((LST_RGBW <= light_subtype) && (TasmotaGlobal.devices_present > 1) && (Settings->param[P_RGB_REMAP] & 128)); // Only on RGBW or RGBCW and SetOption37 128
|
bool split_white = ((LST_RGBW <= light_subtype) && (light_devices > 1) && (Settings->param[P_RGB_REMAP] & 128)); // Only on RGBW or RGBCW and SetOption37 128
|
||||||
|
|
||||||
if ((LST_COLDWARM == light_subtype) || ((LST_RGBCW == light_subtype) && !split_white)) {
|
if ((LST_COLDWARM == light_subtype) || ((LST_RGBCW == light_subtype) && !split_white)) {
|
||||||
WebSliderColdWarm();
|
WebSliderColdWarm();
|
||||||
|
@ -1538,7 +1537,7 @@ void HandleRoot(void) {
|
||||||
uint32_t width = 100;
|
uint32_t width = 100;
|
||||||
WSContentSend_P(PSTR("<tr>"));
|
WSContentSend_P(PSTR("<tr>"));
|
||||||
|
|
||||||
if (button_idx <= TasmotaGlobal.devices_present) {
|
if (button_idx < (light_device + light_devices)) {
|
||||||
bool set_button = ((button_idx <= MAX_BUTTON_TEXT) && strlen(GetWebButton(button_idx -1)));
|
bool set_button = ((button_idx <= MAX_BUTTON_TEXT) && strlen(GetWebButton(button_idx -1)));
|
||||||
char first[2];
|
char first[2];
|
||||||
snprintf_P(first, sizeof(first), PSTR("%s"), PSTR(D_BUTTON_TOGGLE));
|
snprintf_P(first, sizeof(first), PSTR("%s"), PSTR(D_BUTTON_TOGGLE));
|
||||||
|
@ -1564,9 +1563,8 @@ void HandleRoot(void) {
|
||||||
WSContentSend_P(PSTR("</tr>"));
|
WSContentSend_P(PSTR("</tr>"));
|
||||||
}
|
}
|
||||||
} else { // Settings->flag3.pwm_multi_channels - SetOption68 1 - Enable multi-channels PWM instead of Color PWM
|
} else { // Settings->flag3.pwm_multi_channels - SetOption68 1 - Enable multi-channels PWM instead of Color PWM
|
||||||
uint32_t pwm_channels = TasmotaGlobal.devices_present - buttons_non_light;
|
stemp[0] = 'e'; stemp[1] = '0'; stemp[2] = '\0'; // e0
|
||||||
stemp[0] = 'e'; stemp[1] = '0'; stemp[2] = '\0'; // d0
|
for (uint32_t i = 0; i < light_devices; i++) {
|
||||||
for (uint32_t i = 0; i < pwm_channels; i++) {
|
|
||||||
bool set_button = ((button_idx <= MAX_BUTTON_TEXT) && strlen(GetWebButton(button_idx -1)));
|
bool set_button = ((button_idx <= MAX_BUTTON_TEXT) && strlen(GetWebButton(button_idx -1)));
|
||||||
char first[2];
|
char first[2];
|
||||||
snprintf_P(first, sizeof(first), PSTR("%s"), PSTR(D_BUTTON_TOGGLE));
|
snprintf_P(first, sizeof(first), PSTR("%s"), PSTR(D_BUTTON_TOGGLE));
|
||||||
|
@ -1686,7 +1684,7 @@ bool HandleRootStatusRefresh(void) {
|
||||||
char svalue[32]; // Command and number parameter
|
char svalue[32]; // Command and number parameter
|
||||||
char webindex[5]; // WebGetArg name
|
char webindex[5]; // WebGetArg name
|
||||||
|
|
||||||
WebGetArg(PSTR("o"), tmp, sizeof(tmp)); // 1 - 16 Device number for button Toggle or Fanspeed
|
WebGetArg(PSTR("o"), tmp, sizeof(tmp)); // 1 - 32 Device number for button Toggle or Fanspeed
|
||||||
if (strlen(tmp)) {
|
if (strlen(tmp)) {
|
||||||
ShowWebSource(SRC_WEBGUI);
|
ShowWebSource(SRC_WEBGUI);
|
||||||
uint32_t device = atoi(tmp);
|
uint32_t device = atoi(tmp);
|
||||||
|
@ -1726,9 +1724,9 @@ bool HandleRootStatusRefresh(void) {
|
||||||
snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_WHITE " %s"), tmp);
|
snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_WHITE " %s"), tmp);
|
||||||
ExecuteWebCommand(svalue);
|
ExecuteWebCommand(svalue);
|
||||||
}
|
}
|
||||||
uint32_t light_device = LightDevice(); // Channel number offset
|
uint32_t light_device = LightDevice(); // Channel number offset
|
||||||
uint32_t pwm_channels = (TasmotaGlobal.light_type & 7) > LST_MAX ? LST_MAX : (TasmotaGlobal.light_type & 7);
|
uint32_t light_devices = LightDevices(); // Number of channels
|
||||||
for (uint32_t j = 0; j < pwm_channels; j++) {
|
for (uint32_t j = 0; j < light_devices; j++) {
|
||||||
snprintf_P(webindex, sizeof(webindex), PSTR("e%d"), j +1);
|
snprintf_P(webindex, sizeof(webindex), PSTR("e%d"), j +1);
|
||||||
WebGetArg(webindex, tmp, sizeof(tmp)); // 0 - 100 percent
|
WebGetArg(webindex, tmp, sizeof(tmp)); // 0 - 100 percent
|
||||||
if (strlen(tmp)) {
|
if (strlen(tmp)) {
|
||||||
|
@ -1902,21 +1900,12 @@ bool HandleRootStatusRefresh(void) {
|
||||||
} else {
|
} else {
|
||||||
#endif // USE_SONOFF_IFAN
|
#endif // USE_SONOFF_IFAN
|
||||||
|
|
||||||
uint32_t buttons_non_light;
|
if (Web.buttons_non_light_non_shutter <= 8) { // Any non light AND non shutter button
|
||||||
uint32_t buttons_non_light_non_shutter;
|
|
||||||
uint32_t shutter_button_mask;
|
|
||||||
WebGetDeviceCounts(buttons_non_light, buttons_non_light_non_shutter, shutter_button_mask);
|
|
||||||
|
|
||||||
if (buttons_non_light_non_shutter <= 8) { // Any non light AND non shutter button
|
|
||||||
WSContentSend_P(PSTR("{t}<tr>"));
|
WSContentSend_P(PSTR("{t}<tr>"));
|
||||||
uint32_t cols = buttons_non_light_non_shutter;
|
uint32_t cols = Web.buttons_non_light_non_shutter;
|
||||||
uint32_t fontsize = (cols < 5) ? 70 - (cols * 8) : 32;
|
uint32_t fontsize = (cols < 5) ? 70 - (cols * 8) : 32;
|
||||||
for (uint32_t idx = 1; idx <= buttons_non_light; idx++) {
|
for (uint32_t idx = 1; idx <= Web.buttons_non_light_non_shutter; idx++) {
|
||||||
|
if (bitRead(Web.light_shutter_button_mask, idx -1)) { continue; } // Skip non-sequential shutter button
|
||||||
#ifdef USE_SHUTTER
|
|
||||||
if (bitRead(shutter_button_mask, idx -1)) { continue; } // Skip non-sequential shutter button
|
|
||||||
#endif // USE_SHUTTER
|
|
||||||
|
|
||||||
snprintf_P(svalue, sizeof(svalue), PSTR("%d"), bitRead(TasmotaGlobal.power, idx -1));
|
snprintf_P(svalue, sizeof(svalue), PSTR("%d"), bitRead(TasmotaGlobal.power, idx -1));
|
||||||
WSContentSend_P(HTTP_DEVICE_STATE, 100 / cols, (bitRead(TasmotaGlobal.power, idx -1)) ? PSTR("bold") : PSTR("normal"), fontsize,
|
WSContentSend_P(HTTP_DEVICE_STATE, 100 / cols, (bitRead(TasmotaGlobal.power, idx -1)) ? PSTR("bold") : PSTR("normal"), fontsize,
|
||||||
(cols < 5) ? GetStateText(bitRead(TasmotaGlobal.power, idx -1)) : svalue);
|
(cols < 5) ? GetStateText(bitRead(TasmotaGlobal.power, idx -1)) : svalue);
|
||||||
|
|
|
@ -236,6 +236,7 @@ struct LIGHT {
|
||||||
uint8_t random = 0;
|
uint8_t random = 0;
|
||||||
uint8_t subtype = 0; // LST_ subtype
|
uint8_t subtype = 0; // LST_ subtype
|
||||||
uint8_t device = 0;
|
uint8_t device = 0;
|
||||||
|
uint8_t devices = 0;
|
||||||
uint8_t old_power = 1;
|
uint8_t old_power = 1;
|
||||||
uint8_t wakeup_active = 0; // 0=inctive, 1=on-going, 2=about to start, 3=will be triggered next cycle
|
uint8_t wakeup_active = 0; // 0=inctive, 1=on-going, 2=about to start, 3=will be triggered next cycle
|
||||||
uint8_t fixed_color_index = 1;
|
uint8_t fixed_color_index = 1;
|
||||||
|
@ -294,10 +295,7 @@ uint8_t LightDevice(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t LightDevices(void) {
|
uint32_t LightDevices(void) {
|
||||||
if (0 == Light.device) {
|
return Light.devices; // Make external
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return TasmotaGlobal.devices_present - Light.device +1; // Make external
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t min3(uint32_t a, uint32_t b, uint32_t c) {
|
static uint32_t min3(uint32_t a, uint32_t b, uint32_t c) {
|
||||||
|
@ -1256,6 +1254,8 @@ void LightInit(void)
|
||||||
Light.fade_initialized = true; // consider fade intialized starting from black
|
Light.fade_initialized = true; // consider fade intialized starting from black
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Light.devices = TasmotaGlobal.devices_present - Light.device +1; // Last time that devices_present is not increments by display
|
||||||
|
|
||||||
LightUpdateColorMapping();
|
LightUpdateColorMapping();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1687,14 +1687,19 @@ float evaluateExpression(const char * expression, unsigned int len) {
|
||||||
while (index < operators_size) {
|
while (index < operators_size) {
|
||||||
if (priority == pgm_read_byte(kExpressionOperatorsPriorities + operators[index])) { // Need to calculate the operator first
|
if (priority == pgm_read_byte(kExpressionOperatorsPriorities + operators[index])) { // Need to calculate the operator first
|
||||||
// Get current object value and remove the next object with current operator
|
// Get current object value and remove the next object with current operator
|
||||||
|
|
||||||
|
// AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: index %d, v1 '%4_f', v2 '%4_f', op %d"), index, &object_values[index], &object_values[index + 1], operators[index]);
|
||||||
|
|
||||||
va = calculateTwoValues(object_values[index], object_values[index + 1], operators[index]);
|
va = calculateTwoValues(object_values[index], object_values[index + 1], operators[index]);
|
||||||
uint32_t i = index;
|
uint32_t i = index;
|
||||||
while (i <= operators_size) {
|
while (i <= operators_size) {
|
||||||
operators[i++] = operators[i]; // operators.remove(index)
|
// operators[i++] = operators[i]; // operators.remove(index) - Fails on ESP32 (#22636)
|
||||||
|
operators[i] = operators[i +1]; // operators.remove(index)
|
||||||
|
i++;
|
||||||
object_values[i] = object_values[i +1]; // object_values.remove(index + 1)
|
object_values[i] = object_values[i +1]; // object_values.remove(index + 1)
|
||||||
}
|
}
|
||||||
operators_size--;
|
operators_size--;
|
||||||
object_values[index] = va; // Replace the current value with the result
|
object_values[index] = va; // Replace the current value with the result
|
||||||
|
|
||||||
// AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: Intermediate '%4_f'"), &object_values[index]);
|
// AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: Intermediate '%4_f'"), &object_values[index]);
|
||||||
|
|
||||||
|
|
|
@ -270,7 +270,7 @@ bool disp_subscribed = false;
|
||||||
/*********************************************************************************************/
|
/*********************************************************************************************/
|
||||||
|
|
||||||
uint32_t DisplayDevices(void) {
|
uint32_t DisplayDevices(void) {
|
||||||
return (disp_device);
|
return (disp_device) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*********************************************************************************************/
|
/*********************************************************************************************/
|
||||||
|
|
|
@ -76,20 +76,8 @@ extern "C" {
|
||||||
be_getmember(vm, 1, "_p");
|
be_getmember(vm, 1, "_p");
|
||||||
void * strip = (void*) be_tocomptr(vm, -1);
|
void * strip = (void*) be_tocomptr(vm, -1);
|
||||||
be_pop(vm, 1);
|
be_pop(vm, 1);
|
||||||
if (strip == nullptr) {
|
|
||||||
be_raise(vm, "internal_error", "tasmotaled object not initialized");
|
|
||||||
}
|
|
||||||
return strip;
|
return strip;
|
||||||
}
|
}
|
||||||
int32_t be_get_leds_type(bvm *vm) {
|
|
||||||
be_getmember(vm, 1, "_t");
|
|
||||||
int32_t type = be_toint(vm, -1);
|
|
||||||
be_pop(vm, 1);
|
|
||||||
if (type < 0) {
|
|
||||||
be_raise(vm, "internal_error", "invalid leds type");
|
|
||||||
}
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
int be_tasmotaled_call_native(bvm *vm);
|
int be_tasmotaled_call_native(bvm *vm);
|
||||||
int be_tasmotaled_call_native(bvm *vm) {
|
int be_tasmotaled_call_native(bvm *vm) {
|
||||||
|
@ -119,7 +107,6 @@ extern "C" {
|
||||||
// if GPIO is '-1'
|
// if GPIO is '-1'
|
||||||
led_type = 0;
|
led_type = 0;
|
||||||
Ws2812InitStrip(); // ensure the tasmotaled object is initialized, because Berry code runs before the driver is initialized
|
Ws2812InitStrip(); // ensure the tasmotaled object is initialized, because Berry code runs before the driver is initialized
|
||||||
// strip = Ws2812GetStrip(); TODO
|
|
||||||
} else {
|
} else {
|
||||||
if (led_type < 1) { led_type = 1; }
|
if (led_type < 1) { led_type = 1; }
|
||||||
TasmotaLEDPusher * pusher = TasmotaLEDPusher::Create(hardware, gpio);
|
TasmotaLEDPusher * pusher = TasmotaLEDPusher::Create(hardware, gpio);
|
||||||
|
@ -130,29 +117,20 @@ extern "C" {
|
||||||
strip->SetPusher(pusher);
|
strip->SetPusher(pusher);
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddLog(LOG_LEVEL_DEBUG, "LED: leds %i gpio %i type %i", leds, gpio, led_type);
|
|
||||||
// store type in attribute `_t`
|
|
||||||
be_pushint(vm, led_type);
|
|
||||||
be_setmember(vm, 1, "_t");
|
|
||||||
be_pop(vm, 1);
|
|
||||||
|
|
||||||
be_pushcomptr(vm, (void*) strip); // if native driver, it is NULL
|
be_pushcomptr(vm, (void*) strip); // if native driver, it is NULL
|
||||||
be_setmember(vm, 1, "_p");
|
be_setmember(vm, 1, "_p");
|
||||||
be_pop(vm, 1);
|
be_pop(vm, 1);
|
||||||
be_pushnil(vm);
|
be_pushnil(vm);
|
||||||
} else {
|
} else {
|
||||||
// all other commands need a valid tasmotaled pointer
|
// all other commands need a valid tasmotaled pointer
|
||||||
int32_t leds_type = be_get_leds_type(vm);
|
|
||||||
TasmotaLED * strip = (TasmotaLED*) be_get_tasmotaled(vm); // raises an exception if pointer is invalid
|
TasmotaLED * strip = (TasmotaLED*) be_get_tasmotaled(vm); // raises an exception if pointer is invalid
|
||||||
// detect native driver
|
// detect native driver means strip == nullptr
|
||||||
bool native = (leds_type == 0);
|
|
||||||
|
|
||||||
be_pushnil(vm); // push a default `nil` return value
|
be_pushnil(vm); // push a default `nil` return value
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case 1: // # 01 : begin void -> void
|
case 1: // # 01 : begin void -> void
|
||||||
if (native) Ws2812Begin();
|
if (strip) strip->Begin();
|
||||||
else if (strip) strip->Begin();
|
else Ws2812Begin();
|
||||||
break;
|
break;
|
||||||
case 2: // # 02 : show void -> void
|
case 2: // # 02 : show void -> void
|
||||||
{
|
{
|
||||||
|
@ -176,43 +154,40 @@ extern "C" {
|
||||||
}
|
}
|
||||||
uint32_t pixels_size; // number of bytes to push
|
uint32_t pixels_size; // number of bytes to push
|
||||||
bool update_completed = false;
|
bool update_completed = false;
|
||||||
if (native) {
|
if (strip) {
|
||||||
|
strip->Show();
|
||||||
|
pixels_size = strip->PixelCount() * strip->PixelSize();
|
||||||
|
update_completed = strip->CanShow();
|
||||||
|
} else {
|
||||||
Ws2812Show();
|
Ws2812Show();
|
||||||
pixels_size = Ws2812PixelsSize();
|
pixels_size = Ws2812PixelsSize();
|
||||||
update_completed =Ws2812CanShow();
|
update_completed =Ws2812CanShow();
|
||||||
}
|
}
|
||||||
else if (strip) {
|
|
||||||
strip->Show();
|
|
||||||
pixels_size = strip->PixelCount() * strip->PixelSize();
|
|
||||||
update_completed = strip->CanShow();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 3: // # 03 : CanShow void -> bool
|
case 3: // # 03 : CanShow void -> bool
|
||||||
if (native) be_pushbool(vm, Ws2812CanShow());
|
if (strip) be_pushbool(vm, strip->CanShow());
|
||||||
else if (strip) be_pushbool(vm, strip->CanShow());
|
else be_pushbool(vm, Ws2812CanShow());
|
||||||
break;
|
break;
|
||||||
case 4: // # 04 : IsDirty void -> bool
|
case 4: // # 04 : IsDirty void -> bool
|
||||||
if (native) be_pushbool(vm, Ws2812IsDirty());
|
if (strip) be_pushbool(vm, strip->IsDirty());
|
||||||
else if (strip) be_pushbool(vm, strip->IsDirty());
|
else be_pushbool(vm, Ws2812IsDirty());
|
||||||
break;
|
break;
|
||||||
case 5: // # 05 : Dirty void -> void
|
case 5: // # 05 : Dirty void -> void
|
||||||
if (native) Ws2812Dirty();
|
if (strip) strip->Dirty();
|
||||||
else if (strip) strip->Dirty();
|
else Ws2812Dirty();
|
||||||
break;
|
break;
|
||||||
case 6: // # 06 : Pixels void -> bytes() (mapped to the buffer)
|
case 6: // # 06 : Pixels void -> bytes() (mapped to the buffer)
|
||||||
{
|
if (strip) be_pushcomptr(vm, strip->Pixels());
|
||||||
if (native) be_pushcomptr(vm, Ws2812Pixels());
|
else be_pushcomptr(vm, Ws2812Pixels());
|
||||||
else if (strip) be_pushcomptr(vm, strip->Pixels());
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 7: // # 07 : PixelSize void -> int
|
case 7: // # 07 : PixelSize void -> int
|
||||||
if (native) be_pushint(vm, Ws2812PixelSize());
|
if (strip) be_pushint(vm, strip->PixelSize());
|
||||||
else if (strip) be_pushint(vm, strip->PixelSize());
|
else be_pushint(vm, Ws2812PixelSize());
|
||||||
break;
|
break;
|
||||||
case 8: // # 08 : PixelCount void -> int
|
case 8: // # 08 : PixelCount void -> int
|
||||||
if (native) be_pushint(vm, Ws2812PixelCount());
|
if (strip) be_pushint(vm, strip->PixelCount());
|
||||||
else if (strip) be_pushint(vm, strip->PixelCount());
|
else be_pushint(vm, Ws2812PixelCount());
|
||||||
break;
|
break;
|
||||||
case 9: // # 09 : ClearTo (color:??) -> void
|
case 9: // # 09 : ClearTo (color:??) -> void
|
||||||
{
|
{
|
||||||
|
@ -227,11 +202,11 @@ extern "C" {
|
||||||
if (from < 0) { from = 0; }
|
if (from < 0) { from = 0; }
|
||||||
if (len < 0) { len = 0; }
|
if (len < 0) { len = 0; }
|
||||||
|
|
||||||
if (native) Ws2812ClearTo(r, g, b, w, from, from + len - 1);
|
if (strip) strip->ClearTo(rgbw, from, from + len - 1);
|
||||||
else if (strip) strip->ClearTo(rgbw, from, from + len - 1);
|
else Ws2812ClearTo(r, g, b, w, from, from + len - 1);
|
||||||
} else {
|
} else {
|
||||||
if (native) Ws2812ClearTo(r, g, b, w, -1, -1);
|
if (strip) strip->ClearTo(rgbw);
|
||||||
else if (strip) strip->ClearTo(rgbw);
|
else Ws2812ClearTo(r, g, b, w, -1, -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -239,24 +214,24 @@ extern "C" {
|
||||||
{
|
{
|
||||||
int32_t idx = be_toint(vm, 3);
|
int32_t idx = be_toint(vm, 3);
|
||||||
uint32_t wrgb = be_toint(vm, 4);
|
uint32_t wrgb = be_toint(vm, 4);
|
||||||
if (native) {
|
if (strip) {
|
||||||
|
strip->SetPixelColor(idx, wrgb);
|
||||||
|
} else {
|
||||||
uint8_t w = (wrgb >> 24) & 0xFF;
|
uint8_t w = (wrgb >> 24) & 0xFF;
|
||||||
uint8_t r = (wrgb >> 16) & 0xFF;
|
uint8_t r = (wrgb >> 16) & 0xFF;
|
||||||
uint8_t g = (wrgb >> 8) & 0xFF;
|
uint8_t g = (wrgb >> 8) & 0xFF;
|
||||||
uint8_t b = (wrgb ) & 0xFF;
|
uint8_t b = (wrgb ) & 0xFF;
|
||||||
Ws2812SetPixelColor(idx, r, g, b, w);
|
Ws2812SetPixelColor(idx, r, g, b, w);
|
||||||
} else if (strip) {
|
|
||||||
strip->SetPixelColor(idx, wrgb);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 11: // # 11 : GetPixelColor (idx:int) -> color:int wrgb
|
case 11: // # 11 : GetPixelColor (idx:int) -> color:int wrgb
|
||||||
{
|
{
|
||||||
int32_t idx = be_toint(vm, 3);
|
int32_t idx = be_toint(vm, 3);
|
||||||
if (native) {
|
if (strip) {
|
||||||
be_pushint(vm, Ws2812GetPixelColor(idx));
|
|
||||||
} else if (strip) {
|
|
||||||
be_pushint(vm, strip->GetPixelColor(idx));
|
be_pushint(vm, strip->GetPixelColor(idx));
|
||||||
|
} else {
|
||||||
|
be_pushint(vm, Ws2812GetPixelColor(idx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
* Supported template fields:
|
* Supported template fields:
|
||||||
* NAME - Template name
|
* NAME - Template name
|
||||||
* BASE - Optional. 0 = use relative buttons and switches (default), 1 = use absolute buttons and switches
|
* BASE - Optional. 0 = use relative buttons and switches (default), 1 = use absolute buttons and switches
|
||||||
|
* IOCON - Optional. IOCON I/O Expander configuration register (bitmap: 0 MIRROR 0 DISSLW HAEN ODR INTPOL 0. Default 0b01011000 = 0x58)
|
||||||
* GPIO - Sequential list of pin 1 and up with configured GPIO function
|
* GPIO - Sequential list of pin 1 and up with configured GPIO function
|
||||||
* Function Code Description
|
* Function Code Description
|
||||||
* ------------------- -------- ----------------------------------------
|
* ------------------- -------- ----------------------------------------
|
||||||
|
@ -60,6 +61,9 @@
|
||||||
*
|
*
|
||||||
* Buttons and relays B1 B2 B3 B4 B5 B6 B7 B8 R1 R2 R3 R4 R5 R6 R7 R8
|
* Buttons and relays B1 B2 B3 B4 B5 B6 B7 B8 R1 R2 R3 R4 R5 R6 R7 R8
|
||||||
* {"NAME":"MCP23017 A=B1-8, B=R1-8","GPIO":[32,33,34,35,36,37,38,39,224,225,226,227,228,229,230,231]}
|
* {"NAME":"MCP23017 A=B1-8, B=R1-8","GPIO":[32,33,34,35,36,37,38,39,224,225,226,227,228,229,230,231]}
|
||||||
|
*
|
||||||
|
* Buttons and relays with open-drain INT B1 B2 B3 B4 B5 B6 B7 B8 R1 R2 R3 R4 R5 R6 R7 R8
|
||||||
|
* {"NAME":"MCP23017 A=B1-8, B=R1-8","GPIO":[32,33,34,35,36,37,38,39,224,225,226,227,228,229,230,231],"IOCON":0x5C}
|
||||||
*
|
*
|
||||||
* Buttons, relays, buttons and relays B1 B2 B3 B4 B5 B6 B7 B8 R1 R2 R3 R4 R5 R6 R7 R8 B9 B10B11B12B13B14B15B16R9 R10 R11 R12 R13 R14 R15 R16
|
* Buttons, relays, buttons and relays B1 B2 B3 B4 B5 B6 B7 B8 R1 R2 R3 R4 R5 R6 R7 R8 B9 B10B11B12B13B14B15B16R9 R10 R11 R12 R13 R14 R15 R16
|
||||||
* {"NAME":"MCP23017 A=B1-8, B=R1-8, C=B9-16, D=R9-16","GPIO":[32,33,34,35,36,37,38,39,224,225,226,227,228,229,230,231,40,41,42,43,44,45,46,47,232,233,234,235,236,237,238,239]}
|
* {"NAME":"MCP23017 A=B1-8, B=R1-8, C=B9-16, D=R9-16","GPIO":[32,33,34,35,36,37,38,39,224,225,226,227,228,229,230,231,40,41,42,43,44,45,46,47,232,233,234,235,236,237,238,239]}
|
||||||
|
@ -90,6 +94,8 @@
|
||||||
* MCP23017 support
|
* MCP23017 support
|
||||||
\*********************************************************************************************/
|
\*********************************************************************************************/
|
||||||
|
|
||||||
|
#define D_JSON_IOCON "IOCON"
|
||||||
|
|
||||||
enum MCP23S08GPIORegisters {
|
enum MCP23S08GPIORegisters {
|
||||||
MCP23X08_IODIR = 0x00,
|
MCP23X08_IODIR = 0x00,
|
||||||
MCP23X08_IPOL = 0x01,
|
MCP23X08_IPOL = 0x01,
|
||||||
|
@ -140,11 +146,25 @@ typedef struct {
|
||||||
uint8_t olatb;
|
uint8_t olatb;
|
||||||
uint8_t address;
|
uint8_t address;
|
||||||
uint8_t interface;
|
uint8_t interface;
|
||||||
uint8_t pins; // 8 (MCP23x08) or 16 (MCP23x17)
|
uint8_t pins; // 8 (MCP23x08) or 16 (MCP23x17)
|
||||||
int8_t pin_cs;
|
int8_t pin_cs;
|
||||||
int8_t pin_int;
|
int8_t pin_int;
|
||||||
} tMcp23xDevice;
|
} tMcp23xDevice;
|
||||||
|
|
||||||
|
typedef union { // Restricted by MISRA-C Rule 18.4 but so useful...
|
||||||
|
uint8_t reg; // Allow bit manipulation using template IOCON
|
||||||
|
struct {
|
||||||
|
uint8_t spare0 : 1; // 0 Unimplemented
|
||||||
|
uint8_t INTPOL : 1; // (0) INT pin active-low. (1) active-high
|
||||||
|
uint8_t ODR : 1; // (0) INT pin active driver output. (1) Open-drain output (overrides INTPOL)
|
||||||
|
uint8_t HAEN : 1; // (1) Hardware Address enabled (MCS23S17 only)
|
||||||
|
uint8_t DISSLW : 1; // (0) SDA output slew rate disabled
|
||||||
|
uint8_t SEQOP : 1; // 0 Sequential operation enabled, address pointer increments
|
||||||
|
uint8_t MIRROR : 1; // (1) INT pins are internally connected
|
||||||
|
uint8_t BANK : 1; // 0 Registers are in the same bank (addresses are sequential) (MCS23x17 only)
|
||||||
|
};
|
||||||
|
} tIOCON;
|
||||||
|
|
||||||
struct MCP230 {
|
struct MCP230 {
|
||||||
tMcp23xDevice device[MCP23XXX_MAX_DEVICES];
|
tMcp23xDevice device[MCP23XXX_MAX_DEVICES];
|
||||||
uint32_t relay_inverted;
|
uint32_t relay_inverted;
|
||||||
|
@ -156,6 +176,7 @@ struct MCP230 {
|
||||||
uint8_t relay_offset;
|
uint8_t relay_offset;
|
||||||
uint8_t button_max;
|
uint8_t button_max;
|
||||||
uint8_t switch_max;
|
uint8_t switch_max;
|
||||||
|
tIOCON iocon;
|
||||||
int8_t button_offset;
|
int8_t button_offset;
|
||||||
int8_t switch_offset;
|
int8_t switch_offset;
|
||||||
bool base;
|
bool base;
|
||||||
|
@ -339,15 +360,15 @@ void MCP23xPinMode(uint8_t pin, uint8_t flags) {
|
||||||
}
|
}
|
||||||
switch (flags) {
|
switch (flags) {
|
||||||
case INPUT:
|
case INPUT:
|
||||||
MCP23xUpdate(pin, true, iodir);
|
MCP23xUpdate(pin, true, iodir); // Pin is configured as an input
|
||||||
MCP23xUpdate(pin, false, gppu);
|
MCP23xUpdate(pin, false, gppu); // Pull-up disabled
|
||||||
break;
|
break;
|
||||||
case INPUT_PULLUP:
|
case INPUT_PULLUP:
|
||||||
MCP23xUpdate(pin, true, iodir);
|
MCP23xUpdate(pin, true, iodir); // Pin is configured as an input
|
||||||
MCP23xUpdate(pin, true, gppu);
|
MCP23xUpdate(pin, true, gppu); // Pull-up enabled
|
||||||
break;
|
break;
|
||||||
case OUTPUT:
|
case OUTPUT:
|
||||||
MCP23xUpdate(pin, false, iodir);
|
MCP23xUpdate(pin, false, iodir); // Pin is configured as an output
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -371,21 +392,21 @@ void MCP23xPinInterruptMode(uint8_t pin, uint8_t interrupt_mode) {
|
||||||
}
|
}
|
||||||
switch (interrupt_mode) {
|
switch (interrupt_mode) {
|
||||||
case MCP23XXX_CHANGE:
|
case MCP23XXX_CHANGE:
|
||||||
MCP23xUpdate(pin, true, gpinten);
|
MCP23xUpdate(pin, true, gpinten); // Enable GPIO input pin for interrupt-on-change event
|
||||||
MCP23xUpdate(pin, false, intcon);
|
MCP23xUpdate(pin, false, intcon); // Pin value is compared against the previous pin value
|
||||||
break;
|
break;
|
||||||
case MCP23XXX_RISING:
|
case MCP23XXX_RISING:
|
||||||
MCP23xUpdate(pin, true, gpinten);
|
MCP23xUpdate(pin, true, gpinten); // Enable GPIO input pin for interrupt-on-change event
|
||||||
MCP23xUpdate(pin, true, intcon);
|
MCP23xUpdate(pin, true, intcon); // Controls how the associated pin value is compared for interrupt-on-change
|
||||||
MCP23xUpdate(pin, true, defval);
|
MCP23xUpdate(pin, false, defval); // If the associated pin level is the opposite from the register bit, an interrupt occurs.
|
||||||
break;
|
break;
|
||||||
case MCP23XXX_FALLING:
|
case MCP23XXX_FALLING:
|
||||||
MCP23xUpdate(pin, true, gpinten);
|
MCP23xUpdate(pin, true, gpinten); // Enable GPIO input pin for interrupt-on-change event
|
||||||
MCP23xUpdate(pin, true, intcon);
|
MCP23xUpdate(pin, true, intcon); // Controls how the associated pin value is compared for interrupt-on-change
|
||||||
MCP23xUpdate(pin, false, defval);
|
MCP23xUpdate(pin, true, defval); // If the associated pin level is the opposite from the register bit, an interrupt occurs.
|
||||||
break;
|
break;
|
||||||
case MCP23XXX_NO_INTERRUPT:
|
case MCP23XXX_NO_INTERRUPT:
|
||||||
MCP23xUpdate(pin, false, gpinten);
|
MCP23xUpdate(pin, false, gpinten); // Disable GPIO input pin for interrupt-on-change event
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -394,7 +415,7 @@ void MCP23xSetPinModes(uint8_t pin, uint8_t flags) {
|
||||||
// pin 0 - 63
|
// pin 0 - 63
|
||||||
MCP23xPinMode(pin, flags);
|
MCP23xPinMode(pin, flags);
|
||||||
if (Mcp23x.device[Mcp23x.chip].pin_int > -1) { // Mcp23x.chip is updated by call to MCP23xPinMode
|
if (Mcp23x.device[Mcp23x.chip].pin_int > -1) { // Mcp23x.chip is updated by call to MCP23xPinMode
|
||||||
MCP23xPinInterruptMode(pin, MCP23XXX_CHANGE);
|
MCP23xPinInterruptMode(pin, (Mcp23x.iocon.ODR) ? MCP23XXX_FALLING : MCP23XXX_CHANGE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -457,6 +478,15 @@ uint32_t MCP23xGetPin(uint32_t lpin) {
|
||||||
|
|
||||||
/*********************************************************************************************/
|
/*********************************************************************************************/
|
||||||
|
|
||||||
|
bool MCP23xAddItem(uint8_t &item) {
|
||||||
|
if (item >= MAX_RELAYS_SET) { // MAX_RELAYS_SET = MAX_SWITCHES_SET = MAX_KEYS_SET = 32
|
||||||
|
AddLog(LOG_LEVEL_INFO, PSTR("MCP: Max reached"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
item++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
String MCP23xTemplateLoadFile(void) {
|
String MCP23xTemplateLoadFile(void) {
|
||||||
String mcptmplt = "";
|
String mcptmplt = "";
|
||||||
#ifdef USE_UFILESYS
|
#ifdef USE_UFILESYS
|
||||||
|
@ -497,7 +527,7 @@ bool MCP23xLoadTemplate(void) {
|
||||||
}
|
}
|
||||||
val = root[PSTR(D_JSON_NAME)];
|
val = root[PSTR(D_JSON_NAME)];
|
||||||
if (val) {
|
if (val) {
|
||||||
AddLog(LOG_LEVEL_DEBUG, PSTR("MCP: Base %d, Template '%s'"), Mcp23x.base, val.getStr());
|
AddLog(LOG_LEVEL_DEBUG, PSTR("MCP: IOCON 0x%02X, Base %d, Template '%s'"), Mcp23x.iocon, Mcp23x.base, val.getStr());
|
||||||
}
|
}
|
||||||
JsonParserArray arr = root[PSTR(D_JSON_GPIO)];
|
JsonParserArray arr = root[PSTR(D_JSON_GPIO)];
|
||||||
if (arr) {
|
if (arr) {
|
||||||
|
@ -507,44 +537,36 @@ bool MCP23xLoadTemplate(void) {
|
||||||
if (!val) { break; }
|
if (!val) { break; }
|
||||||
uint16_t mpin = val.getUInt();
|
uint16_t mpin = val.getUInt();
|
||||||
if (mpin) { // Above GPIO_NONE
|
if (mpin) { // Above GPIO_NONE
|
||||||
if ((mpin >= AGPIO(GPIO_SWT1)) && (mpin < (AGPIO(GPIO_SWT1) + MAX_SWITCHES_SET))) {
|
if ((mpin >= AGPIO(GPIO_SWT1)) && (mpin < (AGPIO(GPIO_SWT1) + MAX_SWITCHES_SET)) && MCP23xAddItem(Mcp23x.switch_max)) {
|
||||||
Mcp23x.switch_max++;
|
|
||||||
MCP23xSetPinModes(pin, INPUT_PULLUP);
|
MCP23xSetPinModes(pin, INPUT_PULLUP);
|
||||||
}
|
}
|
||||||
else if ((mpin >= AGPIO(GPIO_SWT1_NP)) && (mpin < (AGPIO(GPIO_SWT1_NP) + MAX_SWITCHES_SET))) {
|
else if ((mpin >= AGPIO(GPIO_SWT1_NP)) && (mpin < (AGPIO(GPIO_SWT1_NP) + MAX_SWITCHES_SET)) && MCP23xAddItem(Mcp23x.switch_max)) {
|
||||||
mpin -= (AGPIO(GPIO_SWT1_NP) - AGPIO(GPIO_SWT1));
|
mpin -= (AGPIO(GPIO_SWT1_NP) - AGPIO(GPIO_SWT1));
|
||||||
Mcp23x.switch_max++;
|
|
||||||
MCP23xSetPinModes(pin, INPUT);
|
MCP23xSetPinModes(pin, INPUT);
|
||||||
}
|
}
|
||||||
else if ((mpin >= AGPIO(GPIO_KEY1)) && (mpin < (AGPIO(GPIO_KEY1) + MAX_KEYS_SET))) {
|
else if ((mpin >= AGPIO(GPIO_KEY1)) && (mpin < (AGPIO(GPIO_KEY1) + MAX_KEYS_SET)) && MCP23xAddItem(Mcp23x.button_max)) {
|
||||||
Mcp23x.button_max++;
|
|
||||||
MCP23xSetPinModes(pin, INPUT_PULLUP);
|
MCP23xSetPinModes(pin, INPUT_PULLUP);
|
||||||
}
|
}
|
||||||
else if ((mpin >= AGPIO(GPIO_KEY1_NP)) && (mpin < (AGPIO(GPIO_KEY1_NP) + MAX_KEYS_SET))) {
|
else if ((mpin >= AGPIO(GPIO_KEY1_NP)) && (mpin < (AGPIO(GPIO_KEY1_NP) + MAX_KEYS_SET)) && MCP23xAddItem(Mcp23x.button_max)) {
|
||||||
mpin -= (AGPIO(GPIO_KEY1_NP) - AGPIO(GPIO_KEY1));
|
mpin -= (AGPIO(GPIO_KEY1_NP) - AGPIO(GPIO_KEY1));
|
||||||
Mcp23x.button_max++;
|
|
||||||
MCP23xSetPinModes(pin, INPUT);
|
MCP23xSetPinModes(pin, INPUT);
|
||||||
}
|
}
|
||||||
else if ((mpin >= AGPIO(GPIO_KEY1_INV)) && (mpin < (AGPIO(GPIO_KEY1_INV) + MAX_KEYS_SET))) {
|
else if ((mpin >= AGPIO(GPIO_KEY1_INV)) && (mpin < (AGPIO(GPIO_KEY1_INV) + MAX_KEYS_SET)) && MCP23xAddItem(Mcp23x.button_max)) {
|
||||||
bitSet(Mcp23x.button_inverted, mpin - AGPIO(GPIO_KEY1_INV));
|
bitSet(Mcp23x.button_inverted, mpin - AGPIO(GPIO_KEY1_INV));
|
||||||
mpin -= (AGPIO(GPIO_KEY1_INV) - AGPIO(GPIO_KEY1));
|
mpin -= (AGPIO(GPIO_KEY1_INV) - AGPIO(GPIO_KEY1));
|
||||||
Mcp23x.button_max++;
|
|
||||||
MCP23xSetPinModes(pin, INPUT_PULLUP);
|
MCP23xSetPinModes(pin, INPUT_PULLUP);
|
||||||
}
|
}
|
||||||
else if ((mpin >= AGPIO(GPIO_KEY1_INV_NP)) && (mpin < (AGPIO(GPIO_KEY1_INV_NP) + MAX_KEYS_SET))) {
|
else if ((mpin >= AGPIO(GPIO_KEY1_INV_NP)) && (mpin < (AGPIO(GPIO_KEY1_INV_NP) + MAX_KEYS_SET)) && MCP23xAddItem(Mcp23x.button_max)) {
|
||||||
bitSet(Mcp23x.button_inverted, mpin - AGPIO(GPIO_KEY1_INV_NP));
|
bitSet(Mcp23x.button_inverted, mpin - AGPIO(GPIO_KEY1_INV_NP));
|
||||||
mpin -= (AGPIO(GPIO_KEY1_INV_NP) - AGPIO(GPIO_KEY1));
|
mpin -= (AGPIO(GPIO_KEY1_INV_NP) - AGPIO(GPIO_KEY1));
|
||||||
Mcp23x.button_max++;
|
|
||||||
MCP23xSetPinModes(pin, INPUT);
|
MCP23xSetPinModes(pin, INPUT);
|
||||||
}
|
}
|
||||||
else if ((mpin >= AGPIO(GPIO_REL1)) && (mpin < (AGPIO(GPIO_REL1) + MAX_RELAYS_SET))) {
|
else if ((mpin >= AGPIO(GPIO_REL1)) && (mpin < (AGPIO(GPIO_REL1) + MAX_RELAYS_SET)) && MCP23xAddItem(Mcp23x.relay_max)) {
|
||||||
Mcp23x.relay_max++;
|
|
||||||
MCP23xPinMode(pin, OUTPUT);
|
MCP23xPinMode(pin, OUTPUT);
|
||||||
}
|
}
|
||||||
else if ((mpin >= AGPIO(GPIO_REL1_INV)) && (mpin < (AGPIO(GPIO_REL1_INV) + MAX_RELAYS_SET))) {
|
else if ((mpin >= AGPIO(GPIO_REL1_INV)) && (mpin < (AGPIO(GPIO_REL1_INV) + MAX_RELAYS_SET)) && MCP23xAddItem(Mcp23x.relay_max)) {
|
||||||
bitSet(Mcp23x.relay_inverted, mpin - AGPIO(GPIO_REL1_INV));
|
bitSet(Mcp23x.relay_inverted, mpin - AGPIO(GPIO_REL1_INV));
|
||||||
mpin -= (AGPIO(GPIO_REL1_INV) - AGPIO(GPIO_REL1));
|
mpin -= (AGPIO(GPIO_REL1_INV) - AGPIO(GPIO_REL1));
|
||||||
Mcp23x.relay_max++;
|
|
||||||
MCP23xPinMode(pin, OUTPUT);
|
MCP23xPinMode(pin, OUTPUT);
|
||||||
}
|
}
|
||||||
else if (mpin == AGPIO(GPIO_OUTPUT_HI)) {
|
else if (mpin == AGPIO(GPIO_OUTPUT_HI)) {
|
||||||
|
@ -558,14 +580,9 @@ bool MCP23xLoadTemplate(void) {
|
||||||
else { mpin = 0; }
|
else { mpin = 0; }
|
||||||
Mcp23x_gpio_pin[pin] = mpin;
|
Mcp23x_gpio_pin[pin] = mpin;
|
||||||
}
|
}
|
||||||
if ((Mcp23x.switch_max >= MAX_SWITCHES_SET) ||
|
|
||||||
(Mcp23x.button_max >= MAX_KEYS_SET) ||
|
|
||||||
(Mcp23x.relay_max >= MAX_RELAYS_SET)) {
|
|
||||||
AddLog(LOG_LEVEL_INFO, PSTR("MCP: Max reached (S%d/B%d/R%d)"), Mcp23x.switch_max, Mcp23x.button_max, Mcp23x.relay_max);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Mcp23x.max_pins = pin; // Max number of configured pins
|
Mcp23x.max_pins = pin; // Max number of configured pins
|
||||||
|
AddLog(LOG_LEVEL_INFO, PSTR("MCP: Pins %d (S%d/B%d/R%d)"), Mcp23x.max_pins, Mcp23x.switch_max, Mcp23x.button_max, Mcp23x.relay_max);
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddLog(LOG_LEVEL_DEBUG, PSTR("MCP: Pins %d, Mcp23x_gpio_pin %*_V"), Mcp23x.max_pins, Mcp23x.max_pins, (uint8_t*)Mcp23x_gpio_pin);
|
// AddLog(LOG_LEVEL_DEBUG, PSTR("MCP: Pins %d, Mcp23x_gpio_pin %*_V"), Mcp23x.max_pins, Mcp23x.max_pins, (uint8_t*)Mcp23x_gpio_pin);
|
||||||
|
@ -582,6 +599,10 @@ uint32_t MCP23xTemplateGpio(void) {
|
||||||
JsonParserObject root = parser.getRootObject();
|
JsonParserObject root = parser.getRootObject();
|
||||||
if (!root) { return 0; }
|
if (!root) { return 0; }
|
||||||
|
|
||||||
|
JsonParserToken val = root[PSTR(D_JSON_IOCON)];
|
||||||
|
if (val) {
|
||||||
|
Mcp23x.iocon.reg = val.getUInt() & 0x5E; // Only allow 0 MIRROR 0 DISSLW HAEN ODR INTPOL 0
|
||||||
|
}
|
||||||
JsonParserArray arr = root[PSTR(D_JSON_GPIO)];
|
JsonParserArray arr = root[PSTR(D_JSON_GPIO)];
|
||||||
if (arr.isArray()) {
|
if (arr.isArray()) {
|
||||||
return arr.size(); // Number of requested pins
|
return arr.size(); // Number of requested pins
|
||||||
|
@ -590,6 +611,7 @@ uint32_t MCP23xTemplateGpio(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MCP23xModuleInit(void) {
|
void MCP23xModuleInit(void) {
|
||||||
|
Mcp23x.iocon.reg = 0b01011000; // Default 0x58 = Enable INT mirror, Disable Slew rate, HAEN pins for addressing
|
||||||
int32_t pins_needed = MCP23xTemplateGpio();
|
int32_t pins_needed = MCP23xTemplateGpio();
|
||||||
if (!pins_needed) {
|
if (!pins_needed) {
|
||||||
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("MCP: Invalid template"));
|
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("MCP: Invalid template"));
|
||||||
|
@ -606,7 +628,8 @@ void MCP23xModuleInit(void) {
|
||||||
#endif
|
#endif
|
||||||
while ((Mcp23x.max_devices < MCP23XXX_MAX_DEVICES) && PinUsed(GPIO_MCP23SXX_CS, Mcp23x.max_devices)) {
|
while ((Mcp23x.max_devices < MCP23XXX_MAX_DEVICES) && PinUsed(GPIO_MCP23SXX_CS, Mcp23x.max_devices)) {
|
||||||
Mcp23x.chip = Mcp23x.max_devices;
|
Mcp23x.chip = Mcp23x.max_devices;
|
||||||
Mcp23x.device[Mcp23x.chip].pin_int = (PinUsed(GPIO_MCP23XXX_INT, Mcp23x.chip)) ? Pin(GPIO_MCP23XXX_INT, Mcp23x.chip) : -1;
|
uint32_t pin_int = (Mcp23x.iocon.ODR) ? 0 : Mcp23x.chip; // INT ODR pins are open-drain outputs and supposedly connected together to one GPIO
|
||||||
|
Mcp23x.device[Mcp23x.chip].pin_int = (PinUsed(GPIO_MCP23XXX_INT, pin_int)) ? Pin(GPIO_MCP23XXX_INT, pin_int) : -1;
|
||||||
Mcp23x.device[Mcp23x.chip].pin_cs = Pin(GPIO_MCP23SXX_CS, Mcp23x.max_devices);
|
Mcp23x.device[Mcp23x.chip].pin_cs = Pin(GPIO_MCP23SXX_CS, Mcp23x.max_devices);
|
||||||
digitalWrite(Mcp23x.device[Mcp23x.chip].pin_cs, 1);
|
digitalWrite(Mcp23x.device[Mcp23x.chip].pin_cs, 1);
|
||||||
pinMode(Mcp23x.device[Mcp23x.chip].pin_cs, OUTPUT);
|
pinMode(Mcp23x.device[Mcp23x.chip].pin_cs, OUTPUT);
|
||||||
|
@ -619,12 +642,14 @@ void MCP23xModuleInit(void) {
|
||||||
if (0x00 == buffer) { // MCP23S08
|
if (0x00 == buffer) { // MCP23S08
|
||||||
AddLog(LOG_LEVEL_INFO, PSTR("SPI: MCP23S08 found at CS%d"), Mcp23x.chip +1);
|
AddLog(LOG_LEVEL_INFO, PSTR("SPI: MCP23S08 found at CS%d"), Mcp23x.chip +1);
|
||||||
Mcp23x.device[Mcp23x.chip].pins = 8;
|
Mcp23x.device[Mcp23x.chip].pins = 8;
|
||||||
MCP23xWrite(MCP23X08_IOCON, 0b00011000); // Enable INT mirror, Slew rate disabled, HAEN pins for addressing
|
// MCP23xWrite(MCP23X08_IOCON, 0b00011000); // Slew rate disabled, HAEN pins for addressing
|
||||||
|
MCP23xWrite(MCP23X08_IOCON, Mcp23x.iocon.reg & 0x3E);
|
||||||
Mcp23x.device[Mcp23x.chip].olata = MCP23xRead(MCP23X08_OLAT);
|
Mcp23x.device[Mcp23x.chip].olata = MCP23xRead(MCP23X08_OLAT);
|
||||||
} else if (0x80 == buffer) { // MCP23S17
|
} else if (0x80 == buffer) { // MCP23S17
|
||||||
AddLog(LOG_LEVEL_INFO, PSTR("SPI: MCP23S17 found at CS%d"), Mcp23x.chip +1);
|
AddLog(LOG_LEVEL_INFO, PSTR("SPI: MCP23S17 found at CS%d"), Mcp23x.chip +1);
|
||||||
Mcp23x.device[Mcp23x.chip].pins = 16;
|
Mcp23x.device[Mcp23x.chip].pins = 16;
|
||||||
MCP23xWrite(MCP23X17_IOCONA, 0b01011000); // Enable INT mirror, Slew rate disabled, HAEN pins for addressing
|
// MCP23xWrite(MCP23X17_IOCONA, 0b01011000); // Enable INT mirror, Slew rate disabled, HAEN pins for addressing
|
||||||
|
MCP23xWrite(MCP23X17_IOCONA, Mcp23x.iocon.reg);
|
||||||
Mcp23x.device[Mcp23x.chip].olata = MCP23xRead(MCP23X17_OLATA);
|
Mcp23x.device[Mcp23x.chip].olata = MCP23xRead(MCP23X17_OLATA);
|
||||||
Mcp23x.device[Mcp23x.chip].olatb = MCP23xRead(MCP23X17_OLATB);
|
Mcp23x.device[Mcp23x.chip].olatb = MCP23xRead(MCP23X17_OLATB);
|
||||||
}
|
}
|
||||||
|
@ -641,8 +666,9 @@ void MCP23xModuleInit(void) {
|
||||||
uint8_t mcp23xxx_address = MCP23XXX_ADDR_START;
|
uint8_t mcp23xxx_address = MCP23XXX_ADDR_START;
|
||||||
while ((Mcp23x.max_devices < MCP23XXX_MAX_DEVICES) && (mcp23xxx_address < MCP23XXX_ADDR_END)) {
|
while ((Mcp23x.max_devices < MCP23XXX_MAX_DEVICES) && (mcp23xxx_address < MCP23XXX_ADDR_END)) {
|
||||||
Mcp23x.chip = Mcp23x.max_devices;
|
Mcp23x.chip = Mcp23x.max_devices;
|
||||||
|
uint32_t pin_int = (Mcp23x.iocon.ODR) ? 0 : Mcp23x.chip; // INT pins are open-drain outputs and supposedly connected together to one GPIO
|
||||||
if (I2cSetDevice(mcp23xxx_address)) {
|
if (I2cSetDevice(mcp23xxx_address)) {
|
||||||
Mcp23x.device[Mcp23x.chip].pin_int = (PinUsed(GPIO_MCP23XXX_INT, Mcp23x.chip)) ? Pin(GPIO_MCP23XXX_INT, Mcp23x.chip) : -1;
|
Mcp23x.device[Mcp23x.chip].pin_int = (PinUsed(GPIO_MCP23XXX_INT, pin_int)) ? Pin(GPIO_MCP23XXX_INT, pin_int) : -1;
|
||||||
Mcp23x.device[Mcp23x.chip].interface = MCP23X_I2C;
|
Mcp23x.device[Mcp23x.chip].interface = MCP23X_I2C;
|
||||||
Mcp23x.device[Mcp23x.chip].address = mcp23xxx_address;
|
Mcp23x.device[Mcp23x.chip].address = mcp23xxx_address;
|
||||||
|
|
||||||
|
@ -652,7 +678,8 @@ void MCP23xModuleInit(void) {
|
||||||
if (0x00 == buffer) {
|
if (0x00 == buffer) {
|
||||||
I2cSetActiveFound(mcp23xxx_address, "MCP23008");
|
I2cSetActiveFound(mcp23xxx_address, "MCP23008");
|
||||||
Mcp23x.device[Mcp23x.chip].pins = 8;
|
Mcp23x.device[Mcp23x.chip].pins = 8;
|
||||||
MCP23xWrite(MCP23X08_IOCON, 0b00011000); // Slew rate disabled, HAEN pins for addressing
|
// MCP23xWrite(MCP23X08_IOCON, 0b00011000); // Slew rate disabled, HAEN pins for addressing
|
||||||
|
MCP23xWrite(MCP23X08_IOCON, Mcp23x.iocon.reg & 0x3E);
|
||||||
Mcp23x.device[Mcp23x.chip].olata = MCP23xRead(MCP23X08_OLAT);
|
Mcp23x.device[Mcp23x.chip].olata = MCP23xRead(MCP23X08_OLAT);
|
||||||
Mcp23x.max_devices++;
|
Mcp23x.max_devices++;
|
||||||
}
|
}
|
||||||
|
@ -660,7 +687,8 @@ void MCP23xModuleInit(void) {
|
||||||
I2cSetActiveFound(mcp23xxx_address, "MCP23017");
|
I2cSetActiveFound(mcp23xxx_address, "MCP23017");
|
||||||
Mcp23x.device[Mcp23x.chip].pins = 16;
|
Mcp23x.device[Mcp23x.chip].pins = 16;
|
||||||
MCP23xWrite(MCP23X08_IOCON, 0x00); // Reset bank mode to 0 (MCP23X17_GPINTENB)
|
MCP23xWrite(MCP23X08_IOCON, 0x00); // Reset bank mode to 0 (MCP23X17_GPINTENB)
|
||||||
MCP23xWrite(MCP23X17_IOCONA, 0b01011000); // Enable INT mirror, Slew rate disabled, HAEN pins for addressing
|
// MCP23xWrite(MCP23X17_IOCONA, 0b01011000); // Enable INT mirror, Slew rate disabled, HAEN pins for addressing
|
||||||
|
MCP23xWrite(MCP23X17_IOCONA, Mcp23x.iocon.reg);
|
||||||
Mcp23x.device[Mcp23x.chip].olata = MCP23xRead(MCP23X17_OLATA);
|
Mcp23x.device[Mcp23x.chip].olata = MCP23xRead(MCP23X17_OLATA);
|
||||||
Mcp23x.device[Mcp23x.chip].olatb = MCP23xRead(MCP23X17_OLATB);
|
Mcp23x.device[Mcp23x.chip].olatb = MCP23xRead(MCP23X17_OLATB);
|
||||||
Mcp23x.max_devices++;
|
Mcp23x.max_devices++;
|
||||||
|
@ -691,6 +719,8 @@ void MCP23xModuleInit(void) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("MCP: INT open-drain %d"), Mcp23x.iocon.ODR);
|
||||||
|
|
||||||
Mcp23x.relay_offset = TasmotaGlobal.devices_present;
|
Mcp23x.relay_offset = TasmotaGlobal.devices_present;
|
||||||
Mcp23x.relay_max -= UpdateDevicesPresent(Mcp23x.relay_max);
|
Mcp23x.relay_max -= UpdateDevicesPresent(Mcp23x.relay_max);
|
||||||
|
|
||||||
|
@ -745,8 +775,10 @@ void MCP23xInit(void) {
|
||||||
} else {
|
} else {
|
||||||
gpio = MCP23xRead16(MCP23X17_GPIOA); // Clear MCP23x17 interrupt
|
gpio = MCP23xRead16(MCP23X17_GPIOA); // Clear MCP23x17 interrupt
|
||||||
}
|
}
|
||||||
|
if (Mcp23x.iocon.ODR && Mcp23x.chip) { continue; }
|
||||||
|
// pinMode(Mcp23x.device[Mcp23x.chip].pin_int, (Mcp23x.iocon.ODR) ? INPUT_PULLUP : INPUT);
|
||||||
pinMode(Mcp23x.device[Mcp23x.chip].pin_int, INPUT_PULLUP);
|
pinMode(Mcp23x.device[Mcp23x.chip].pin_int, INPUT_PULLUP);
|
||||||
attachInterrupt(Mcp23x.device[Mcp23x.chip].pin_int, MCP23xInputIsr, CHANGE);
|
attachInterrupt(Mcp23x.device[Mcp23x.chip].pin_int, MCP23xInputIsr, (Mcp23x.iocon.ODR) ? FALLING : CHANGE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue