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]
|
||||
|
||||
## [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
|
||||
- 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)
|
||||
- ESP8266 Device Group exception due to lack of stack space (#22271)
|
||||
|
||||
### Removed
|
||||
|
||||
## [14.3.0.6] 20241116
|
||||
### Added
|
||||
- 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
|
||||
|
||||
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``
|
||||
|
||||
|
@ -104,7 +104,7 @@ Latest released binaries can be downloaded from
|
|||
- https://ota.tasmota.com/tasmota32/release
|
||||
|
||||
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``
|
||||
|
||||
|
@ -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.
|
||||
|
||||
## Changelog v14.4.0 Rudolph
|
||||
## Changelog v14.4.1 Rudolph
|
||||
### Added
|
||||
- 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)
|
||||
|
@ -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 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)
|
||||
- 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)
|
||||
- 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)
|
||||
|
@ -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
|
||||
- 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)
|
||||
- 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 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)
|
||||
|
||||
### 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)
|
||||
- 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)
|
||||
- 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 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-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)
|
||||
|
||||
### Removed
|
||||
|
|
|
@ -767,59 +767,47 @@ extern "C" {
|
|||
xc->done_cert = true; // first cert already processed
|
||||
}
|
||||
|
||||
// **** Start patch Castellucci
|
||||
/*
|
||||
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) {
|
||||
// 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) {
|
||||
uint8_t buf[] = {0, 0, 0, 0};
|
||||
|
||||
if (!compat) {
|
||||
buf[0] = (len >> 24) & 0xff;
|
||||
buf[1] = (len >> 16) & 0xff;
|
||||
buf[2] = (len >> 8) & 0xff;
|
||||
buf[3] = (len >> 0) & 0xff;
|
||||
br_sha1_update(shactx, buf, 4); // length
|
||||
}
|
||||
buf[0] = (len >> 24) & 0xff;
|
||||
buf[1] = (len >> 16) & 0xff;
|
||||
buf[2] = (len >> 8) & 0xff;
|
||||
buf[3] = (len >> 0) & 0xff;
|
||||
br_sha1_update(shactx, buf, 4); // length
|
||||
|
||||
br_sha1_update(shactx, msg, len); // message
|
||||
}
|
||||
|
||||
// Update the received fingerprint based on the certificate's public key.
|
||||
// If `compat` is true, an insecure version of the fingerprint will be
|
||||
// calcualted for compatibility with older versions of Tasmota. Normally,
|
||||
// `compat` should be false.
|
||||
static void pubkeyfingerprint_pubkey_fingerprint(br_x509_pubkeyfingerprint_context *xc, bool compat) {
|
||||
// Calculate the received fingerprint based on the certificate's public key.
|
||||
// The public exponent and modulus are length prefixed to avoid security
|
||||
// vulnerabilities related to ambiguous serialization. Without this, an
|
||||
// attacker can generate alternative public keys which result in the same
|
||||
// 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_sha1_context shactx;
|
||||
|
||||
br_sha1_init(&shactx);
|
||||
|
||||
sha1_update_len(&shactx, "ssh-rsa", 7, compat); // tag
|
||||
sha1_update_len(&shactx, rsakey.e, rsakey.elen, compat); // exponent
|
||||
sha1_update_len(&shactx, rsakey.n, rsakey.nlen, compat); // modulus
|
||||
// The tag string doesn't really matter, but it should differ depending on
|
||||
// key type. Since we only support RSA for now, it's a fixed string.
|
||||
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
|
||||
}
|
||||
// **** End patch Castellucci
|
||||
|
||||
// Callback when complete chain has been parsed.
|
||||
// Return 0 on validation success, !0 on validation error
|
||||
static unsigned pubkeyfingerprint_end_chain(const br_x509_class **ctx) {
|
||||
br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx;
|
||||
// set fingerprint status byte to zero
|
||||
// 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);
|
||||
pubkeyfingerprint_pubkey_fingerprint(xc);
|
||||
if (!xc->fingerprint_all) {
|
||||
if (0 == memcmp_P(xc->pubkey_recv_fingerprint, xc->fingerprint1, 20)) {
|
||||
return 0;
|
||||
|
@ -832,7 +820,6 @@ extern "C" {
|
|||
// Default (no validation at all) or no errors in prior checks = success.
|
||||
return 0;
|
||||
}
|
||||
// **** End patch Castellucci
|
||||
}
|
||||
|
||||
// Return the public key from the validator (set by x509_minimal)
|
||||
|
|
|
@ -152,12 +152,7 @@ class WiFiClientSecure_light : public WiFiClient {
|
|||
bool _insecure; // force fingerprint
|
||||
const uint8_t *_fingerprint1; // fingerprint1 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[21]; // fingerprint received
|
||||
// **** End patch Castellucci
|
||||
|
||||
unsigned char *_recvapp_buf;
|
||||
size_t _recvapp_len;
|
||||
|
|
|
@ -110,6 +110,14 @@ class Animate_core
|
|||
self.strip.clear()
|
||||
end
|
||||
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
|
||||
var animators = self.animators
|
||||
var idx = 0
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
* Generated code, don't edit *
|
||||
\********************************************************************/
|
||||
#include "be_constobj.h"
|
||||
// compact class 'Animate_core' ktab size: 48, total: 98 (saved 400 bytes)
|
||||
static const bvalue be_ktab_class_Animate_core[48] = {
|
||||
// compact class 'Animate_core' ktab size: 52, total: 104 (saved 416 bytes)
|
||||
static const bvalue be_ktab_class_Animate_core[52] = {
|
||||
/* K0 */ be_nested_str_weak(stop),
|
||||
/* K1 */ be_nested_str_weak(strip),
|
||||
/* 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),
|
||||
/* K44 */ be_nested_str_weak(set_back_color),
|
||||
/* K45 */ be_nested_str_weak(add_animator),
|
||||
/* K46 */ be_nested_str_weak(start),
|
||||
/* K47 */ be_nested_str_weak(add_fast_loop),
|
||||
/* K46 */ be_nested_str_weak(get_animate),
|
||||
/* 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_nested_proto(
|
||||
6, /* nstack */
|
||||
8, /* nstack */
|
||||
1, /* argc */
|
||||
10, /* varg */
|
||||
0, /* has upvals */
|
||||
|
@ -727,27 +731,47 @@ be_local_closure(class_Animate_core_start, /* name */
|
|||
&be_ktab_class_Animate_core, /* shared constants */
|
||||
be_str_weak(start),
|
||||
&be_const_str_solidified,
|
||||
( &(const binstruction[20]) { /* code */
|
||||
0x50040200, // 0000 LDBOOL R1 1 0
|
||||
0x90021601, // 0001 SETMBR R0 K11 R1
|
||||
0x8804010C, // 0002 GETMBR R1 R0 K12
|
||||
0x58080007, // 0003 LDCONST R2 K7
|
||||
0x600C000C, // 0004 GETGBL R3 G12
|
||||
0x5C100200, // 0005 MOVE R4 R1
|
||||
0x7C0C0200, // 0006 CALL R3 1
|
||||
0x140C0403, // 0007 LT R3 R2 R3
|
||||
0x780E0004, // 0008 JMPF R3 #000E
|
||||
0x940C0202, // 0009 GETIDX R3 R1 R2
|
||||
0x8C0C072E, // 000A GETMET R3 R3 K46
|
||||
0x7C0C0200, // 000B CALL R3 1
|
||||
0x0008050D, // 000C ADD R2 R2 K13
|
||||
0x7001FFF5, // 000D JMP #0004
|
||||
0x90022707, // 000E SETMBR R0 K19 K7
|
||||
0xB80E0800, // 000F GETNGBL R3 K4
|
||||
0x8C0C072F, // 0010 GETMET R3 R3 K47
|
||||
0x8814010F, // 0011 GETMBR R5 R0 K15
|
||||
0x7C0C0400, // 0012 CALL R3 2
|
||||
0x80000000, // 0013 RET 0
|
||||
( &(const binstruction[40]) { /* code */
|
||||
0x88040101, // 0000 GETMBR R1 R0 K1
|
||||
0x8C04032E, // 0001 GETMET R1 R1 K46
|
||||
0x7C040200, // 0002 CALL R1 1
|
||||
0xA40A5E00, // 0003 IMPORT R2 K47
|
||||
0x4C0C0000, // 0004 LDNIL R3
|
||||
0x200C0203, // 0005 NE R3 R1 R3
|
||||
0x780E0008, // 0006 JMPF R3 #0010
|
||||
0x600C0004, // 0007 GETGBL R3 G4
|
||||
0x5C100200, // 0008 MOVE R4 R1
|
||||
0x7C0C0200, // 0009 CALL R3 1
|
||||
0x1C0C0730, // 000A EQ R3 R3 K48
|
||||
0x780E0003, // 000B JMPF R3 #0010
|
||||
0x200C0200, // 000C NE R3 R1 R0
|
||||
0x780E0001, // 000D JMPF R3 #0010
|
||||
0x8C0C0300, // 000E GETMET R3 R1 K0
|
||||
0x7C0C0200, // 000F CALL R3 1
|
||||
0x880C0101, // 0010 GETMBR R3 R0 K1
|
||||
0x8C0C0731, // 0011 GETMET R3 R3 K49
|
||||
0x5C140000, // 0012 MOVE R5 R0
|
||||
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
|
||||
class be_class_Leds_ntv (scope: global, name: Leds_ntv, strings: weak) {
|
||||
_p, var
|
||||
_t, var
|
||||
|
||||
WS2812_GRB, int(ws2812_grb)
|
||||
SK6812_GRBW, int(sk6812_grbw)
|
||||
|
|
|
@ -28,6 +28,7 @@ class Leds : Leds_ntv
|
|||
var gamma # if true, apply gamma (true is default)
|
||||
var leds # number of leds
|
||||
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
|
||||
# gpio:int (optional) = GPIO for NeoPixel. If not specified, takes the WS2812 gpio
|
||||
# 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)
|
||||
import gpio
|
||||
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
|
||||
self.ctor() # no parameters
|
||||
# in such case, `self._p` is equal to `0`
|
||||
self.leds = self.pixel_count()
|
||||
import light
|
||||
self.bri = light.get()['bri']
|
||||
else
|
||||
# use pure Berry driver
|
||||
self.leds = int(leds)
|
||||
leds = int(leds)
|
||||
self.leds = leds
|
||||
self.bri = 127 # 50% brightness by default
|
||||
|
||||
# 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
|
||||
|
||||
if self._p == nil raise "internal_error", "couldn't not initialize noepixelbus" end
|
||||
|
||||
# call begin
|
||||
self.begin()
|
||||
end
|
||||
|
||||
def clear()
|
||||
|
@ -71,6 +90,13 @@ class Leds : Leds_ntv
|
|||
return self.bri
|
||||
end
|
||||
|
||||
def set_animate(animate)
|
||||
self.animate = animate
|
||||
end
|
||||
def get_animate()
|
||||
return self.animate
|
||||
end
|
||||
|
||||
def ctor(leds, gpio_phy, typ, hardware)
|
||||
if gpio_phy == nil
|
||||
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
|
||||
|
||||
|
||||
pattern = (r'''(?<=module\()[^"].*''') # module??
|
||||
pattern = (r'''(?<=module\([\"\']).*[\"\']''') # module??
|
||||
result = re.findall(pattern,code)
|
||||
if len(result) > 0:
|
||||
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_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_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_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_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}";
|
||||
|
@ -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[] 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_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_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_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_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_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_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_BLUE[] PROGMEM = "{s}%s " D_COLOR_BLUE "{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_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_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}";
|
||||
|
@ -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_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_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_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}";
|
||||
|
|
|
@ -22,6 +22,6 @@
|
|||
|
||||
#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_
|
||||
|
|
|
@ -211,7 +211,7 @@
|
|||
#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_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
|
||||
// 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_BACKGROUND "#999" // [WebColor18] Config timer tab background color - Dark 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 -----------------------------------------
|
||||
#define KNX_ENABLED false // [Knx_Enabled] Enable KNX protocol
|
||||
|
|
|
@ -484,6 +484,8 @@ ESP8266WebServer *Webserver;
|
|||
struct WEB {
|
||||
String chunk_buffer = ""; // Could be max 2 * CHUNKED_BUFFER_SIZE
|
||||
uint32_t upload_size = 0;
|
||||
uint32_t light_shutter_button_mask;
|
||||
uint32_t buttons_non_light_non_shutter;
|
||||
uint32_t slider_update_time = 0;
|
||||
int slider[LST_MAX];
|
||||
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) {
|
||||
buttons_non_light = TasmotaGlobal.devices_present;
|
||||
void WebGetDeviceCounts(void) {
|
||||
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
|
||||
// Chk for reduced toggle buttons used by lights
|
||||
if (TasmotaGlobal.light_type) {
|
||||
// Find and skip light buttons (Lights are controlled by the last TasmotaGlobal.devices_present (or 2))
|
||||
buttons_non_light = LightDevice() -1;
|
||||
// Find and skip light buttons
|
||||
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
|
||||
|
||||
buttons_non_light_non_shutter = buttons_non_light;
|
||||
shutter_button_mask = 0; // Bitmask for each button
|
||||
#ifdef USE_SHUTTER
|
||||
// Chk for reduced toggle buttons used by shutters
|
||||
// Find and skip dedicated shutter buttons
|
||||
if (buttons_non_light && Settings->flag3.shutter_mode) { // SetOption80 - Enable shutter support
|
||||
for (uint32_t button_idx = 1; button_idx <= buttons_non_light; button_idx++) {
|
||||
if (Settings->flag3.shutter_mode) { // SetOption80 - Enable shutter support
|
||||
// Find and skip dedicated shutter buttons
|
||||
for (uint32_t button_idx = 1; button_idx <= TasmotaGlobal.devices_present; button_idx++) {
|
||||
if (IsShutterWebButton(button_idx) != 0) {
|
||||
buttons_non_light_non_shutter--;
|
||||
shutter_button_mask |= (1 << (button_idx -1)); // Set button bit in bitmask
|
||||
Web.buttons_non_light_non_shutter--;
|
||||
Web.light_shutter_button_mask |= (1 << (button_idx -1)); // Set button bit in bitmask
|
||||
}
|
||||
}
|
||||
}
|
||||
#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
|
||||
|
@ -1366,13 +1372,10 @@ void HandleRoot(void) {
|
|||
#ifndef FIRMWARE_MINIMAL
|
||||
|
||||
if (TasmotaGlobal.devices_present) {
|
||||
uint32_t buttons_non_light;
|
||||
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;
|
||||
WebGetDeviceCounts();
|
||||
|
||||
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(PSTR("<tr>"));
|
||||
|
||||
|
@ -1391,18 +1394,14 @@ void HandleRoot(void) {
|
|||
#endif // USE_SONOFF_IFAN
|
||||
|
||||
const uint32_t max_columns = 8;
|
||||
uint32_t rows = buttons_non_light_non_shutter / max_columns;
|
||||
if (buttons_non_light_non_shutter % max_columns) { rows++; }
|
||||
uint32_t cols = buttons_non_light_non_shutter / rows;
|
||||
if (buttons_non_light_non_shutter % rows) { cols++; }
|
||||
uint32_t rows = Web.buttons_non_light_non_shutter / max_columns;
|
||||
if (Web.buttons_non_light_non_shutter % max_columns) { rows++; }
|
||||
uint32_t cols = Web.buttons_non_light_non_shutter / rows;
|
||||
if (Web.buttons_non_light_non_shutter % rows) { cols++; }
|
||||
|
||||
uint32_t button_ptr = 0;
|
||||
for (button_idx = 1; button_idx <= buttons_non_light; button_idx++) {
|
||||
|
||||
#ifdef USE_SHUTTER
|
||||
if (bitRead(shutter_button_mask, button_idx -1)) { continue; } // Skip non-sequential shutter button
|
||||
#endif // USE_SHUTTER
|
||||
|
||||
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
|
||||
bool set_button = ((button_idx <= MAX_BUTTON_TEXT) && strlen(GetWebButton(button_idx -1)));
|
||||
snprintf_P(stemp, sizeof(stemp), PSTR(" %d"), 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("</table>"));
|
||||
|
||||
if (1 == button_idx) { // No power/display button
|
||||
button_idx = shutter_button_idx +2;
|
||||
}
|
||||
}
|
||||
#endif // USE_SHUTTER
|
||||
|
||||
#ifdef USE_LIGHT
|
||||
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%%'>"
|
||||
|
||||
uint8_t light_subtype = TasmotaGlobal.light_type &7;
|
||||
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)) {
|
||||
WebSliderColdWarm();
|
||||
|
@ -1538,7 +1537,7 @@ void HandleRoot(void) {
|
|||
uint32_t width = 100;
|
||||
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)));
|
||||
char first[2];
|
||||
snprintf_P(first, sizeof(first), PSTR("%s"), PSTR(D_BUTTON_TOGGLE));
|
||||
|
@ -1564,9 +1563,8 @@ void HandleRoot(void) {
|
|||
WSContentSend_P(PSTR("</tr>"));
|
||||
}
|
||||
} 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'; // d0
|
||||
for (uint32_t i = 0; i < pwm_channels; i++) {
|
||||
stemp[0] = 'e'; stemp[1] = '0'; stemp[2] = '\0'; // e0
|
||||
for (uint32_t i = 0; i < light_devices; i++) {
|
||||
bool set_button = ((button_idx <= MAX_BUTTON_TEXT) && strlen(GetWebButton(button_idx -1)));
|
||||
char first[2];
|
||||
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 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)) {
|
||||
ShowWebSource(SRC_WEBGUI);
|
||||
uint32_t device = atoi(tmp);
|
||||
|
@ -1726,9 +1724,9 @@ bool HandleRootStatusRefresh(void) {
|
|||
snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_WHITE " %s"), tmp);
|
||||
ExecuteWebCommand(svalue);
|
||||
}
|
||||
uint32_t light_device = LightDevice(); // Channel number offset
|
||||
uint32_t pwm_channels = (TasmotaGlobal.light_type & 7) > LST_MAX ? LST_MAX : (TasmotaGlobal.light_type & 7);
|
||||
for (uint32_t j = 0; j < pwm_channels; j++) {
|
||||
uint32_t light_device = LightDevice(); // Channel number offset
|
||||
uint32_t light_devices = LightDevices(); // Number of channels
|
||||
for (uint32_t j = 0; j < light_devices; j++) {
|
||||
snprintf_P(webindex, sizeof(webindex), PSTR("e%d"), j +1);
|
||||
WebGetArg(webindex, tmp, sizeof(tmp)); // 0 - 100 percent
|
||||
if (strlen(tmp)) {
|
||||
|
@ -1902,21 +1900,12 @@ bool HandleRootStatusRefresh(void) {
|
|||
} else {
|
||||
#endif // USE_SONOFF_IFAN
|
||||
|
||||
uint32_t buttons_non_light;
|
||||
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
|
||||
if (Web.buttons_non_light_non_shutter <= 8) { // Any non light AND non shutter button
|
||||
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;
|
||||
for (uint32_t idx = 1; idx <= buttons_non_light; idx++) {
|
||||
|
||||
#ifdef USE_SHUTTER
|
||||
if (bitRead(shutter_button_mask, idx -1)) { continue; } // Skip non-sequential shutter button
|
||||
#endif // USE_SHUTTER
|
||||
|
||||
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
|
||||
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,
|
||||
(cols < 5) ? GetStateText(bitRead(TasmotaGlobal.power, idx -1)) : svalue);
|
||||
|
|
|
@ -236,6 +236,7 @@ struct LIGHT {
|
|||
uint8_t random = 0;
|
||||
uint8_t subtype = 0; // LST_ subtype
|
||||
uint8_t device = 0;
|
||||
uint8_t devices = 0;
|
||||
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 fixed_color_index = 1;
|
||||
|
@ -294,10 +295,7 @@ uint8_t LightDevice(void)
|
|||
}
|
||||
|
||||
uint32_t LightDevices(void) {
|
||||
if (0 == Light.device) {
|
||||
return 0;
|
||||
}
|
||||
return TasmotaGlobal.devices_present - Light.device +1; // Make external
|
||||
return Light.devices; // Make external
|
||||
}
|
||||
|
||||
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.devices = TasmotaGlobal.devices_present - Light.device +1; // Last time that devices_present is not increments by display
|
||||
|
||||
LightUpdateColorMapping();
|
||||
}
|
||||
|
||||
|
|
|
@ -1687,14 +1687,19 @@ float evaluateExpression(const char * expression, unsigned int len) {
|
|||
while (index < operators_size) {
|
||||
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
|
||||
|
||||
// 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]);
|
||||
uint32_t i = index;
|
||||
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)
|
||||
}
|
||||
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]);
|
||||
|
||||
|
|
|
@ -270,7 +270,7 @@ bool disp_subscribed = false;
|
|||
/*********************************************************************************************/
|
||||
|
||||
uint32_t DisplayDevices(void) {
|
||||
return (disp_device);
|
||||
return (disp_device) ? 1 : 0;
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
|
|
@ -76,20 +76,8 @@ extern "C" {
|
|||
be_getmember(vm, 1, "_p");
|
||||
void * strip = (void*) be_tocomptr(vm, -1);
|
||||
be_pop(vm, 1);
|
||||
if (strip == nullptr) {
|
||||
be_raise(vm, "internal_error", "tasmotaled object not initialized");
|
||||
}
|
||||
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) {
|
||||
|
@ -119,7 +107,6 @@ extern "C" {
|
|||
// if GPIO is '-1'
|
||||
led_type = 0;
|
||||
Ws2812InitStrip(); // ensure the tasmotaled object is initialized, because Berry code runs before the driver is initialized
|
||||
// strip = Ws2812GetStrip(); TODO
|
||||
} else {
|
||||
if (led_type < 1) { led_type = 1; }
|
||||
TasmotaLEDPusher * pusher = TasmotaLEDPusher::Create(hardware, gpio);
|
||||
|
@ -130,29 +117,20 @@ extern "C" {
|
|||
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_setmember(vm, 1, "_p");
|
||||
be_pop(vm, 1);
|
||||
be_pushnil(vm);
|
||||
} else {
|
||||
// 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
|
||||
// detect native driver
|
||||
bool native = (leds_type == 0);
|
||||
|
||||
// detect native driver means strip == nullptr
|
||||
be_pushnil(vm); // push a default `nil` return value
|
||||
|
||||
switch (cmd) {
|
||||
case 1: // # 01 : begin void -> void
|
||||
if (native) Ws2812Begin();
|
||||
else if (strip) strip->Begin();
|
||||
if (strip) strip->Begin();
|
||||
else Ws2812Begin();
|
||||
break;
|
||||
case 2: // # 02 : show void -> void
|
||||
{
|
||||
|
@ -176,43 +154,40 @@ extern "C" {
|
|||
}
|
||||
uint32_t pixels_size; // number of bytes to push
|
||||
bool update_completed = false;
|
||||
if (native) {
|
||||
if (strip) {
|
||||
strip->Show();
|
||||
pixels_size = strip->PixelCount() * strip->PixelSize();
|
||||
update_completed = strip->CanShow();
|
||||
} else {
|
||||
Ws2812Show();
|
||||
pixels_size = Ws2812PixelsSize();
|
||||
update_completed =Ws2812CanShow();
|
||||
}
|
||||
else if (strip) {
|
||||
strip->Show();
|
||||
pixels_size = strip->PixelCount() * strip->PixelSize();
|
||||
update_completed = strip->CanShow();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 3: // # 03 : CanShow void -> bool
|
||||
if (native) be_pushbool(vm, Ws2812CanShow());
|
||||
else if (strip) be_pushbool(vm, strip->CanShow());
|
||||
if (strip) be_pushbool(vm, strip->CanShow());
|
||||
else be_pushbool(vm, Ws2812CanShow());
|
||||
break;
|
||||
case 4: // # 04 : IsDirty void -> bool
|
||||
if (native) be_pushbool(vm, Ws2812IsDirty());
|
||||
else if (strip) be_pushbool(vm, strip->IsDirty());
|
||||
if (strip) be_pushbool(vm, strip->IsDirty());
|
||||
else be_pushbool(vm, Ws2812IsDirty());
|
||||
break;
|
||||
case 5: // # 05 : Dirty void -> void
|
||||
if (native) Ws2812Dirty();
|
||||
else if (strip) strip->Dirty();
|
||||
if (strip) strip->Dirty();
|
||||
else Ws2812Dirty();
|
||||
break;
|
||||
case 6: // # 06 : Pixels void -> bytes() (mapped to the buffer)
|
||||
{
|
||||
if (native) be_pushcomptr(vm, Ws2812Pixels());
|
||||
else if (strip) be_pushcomptr(vm, strip->Pixels());
|
||||
}
|
||||
if (strip) be_pushcomptr(vm, strip->Pixels());
|
||||
else be_pushcomptr(vm, Ws2812Pixels());
|
||||
break;
|
||||
case 7: // # 07 : PixelSize void -> int
|
||||
if (native) be_pushint(vm, Ws2812PixelSize());
|
||||
else if (strip) be_pushint(vm, strip->PixelSize());
|
||||
if (strip) be_pushint(vm, strip->PixelSize());
|
||||
else be_pushint(vm, Ws2812PixelSize());
|
||||
break;
|
||||
case 8: // # 08 : PixelCount void -> int
|
||||
if (native) be_pushint(vm, Ws2812PixelCount());
|
||||
else if (strip) be_pushint(vm, strip->PixelCount());
|
||||
if (strip) be_pushint(vm, strip->PixelCount());
|
||||
else be_pushint(vm, Ws2812PixelCount());
|
||||
break;
|
||||
case 9: // # 09 : ClearTo (color:??) -> void
|
||||
{
|
||||
|
@ -227,11 +202,11 @@ extern "C" {
|
|||
if (from < 0) { from = 0; }
|
||||
if (len < 0) { len = 0; }
|
||||
|
||||
if (native) Ws2812ClearTo(r, g, b, w, from, from + len - 1);
|
||||
else if (strip) strip->ClearTo(rgbw, from, from + len - 1);
|
||||
if (strip) strip->ClearTo(rgbw, from, from + len - 1);
|
||||
else Ws2812ClearTo(r, g, b, w, from, from + len - 1);
|
||||
} else {
|
||||
if (native) Ws2812ClearTo(r, g, b, w, -1, -1);
|
||||
else if (strip) strip->ClearTo(rgbw);
|
||||
if (strip) strip->ClearTo(rgbw);
|
||||
else Ws2812ClearTo(r, g, b, w, -1, -1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -239,24 +214,24 @@ extern "C" {
|
|||
{
|
||||
int32_t idx = be_toint(vm, 3);
|
||||
uint32_t wrgb = be_toint(vm, 4);
|
||||
if (native) {
|
||||
if (strip) {
|
||||
strip->SetPixelColor(idx, wrgb);
|
||||
} else {
|
||||
uint8_t w = (wrgb >> 24) & 0xFF;
|
||||
uint8_t r = (wrgb >> 16) & 0xFF;
|
||||
uint8_t g = (wrgb >> 8) & 0xFF;
|
||||
uint8_t b = (wrgb ) & 0xFF;
|
||||
Ws2812SetPixelColor(idx, r, g, b, w);
|
||||
} else if (strip) {
|
||||
strip->SetPixelColor(idx, wrgb);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 11: // # 11 : GetPixelColor (idx:int) -> color:int wrgb
|
||||
{
|
||||
int32_t idx = be_toint(vm, 3);
|
||||
if (native) {
|
||||
be_pushint(vm, Ws2812GetPixelColor(idx));
|
||||
} else if (strip) {
|
||||
if (strip) {
|
||||
be_pushint(vm, strip->GetPixelColor(idx));
|
||||
} else {
|
||||
be_pushint(vm, Ws2812GetPixelColor(idx));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
* Supported template fields:
|
||||
* NAME - Template name
|
||||
* 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
|
||||
* Function Code Description
|
||||
* ------------------- -------- ----------------------------------------
|
||||
|
@ -61,6 +62,9 @@
|
|||
* 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]}
|
||||
*
|
||||
* 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
|
||||
* {"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
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define D_JSON_IOCON "IOCON"
|
||||
|
||||
enum MCP23S08GPIORegisters {
|
||||
MCP23X08_IODIR = 0x00,
|
||||
MCP23X08_IPOL = 0x01,
|
||||
|
@ -140,11 +146,25 @@ typedef struct {
|
|||
uint8_t olatb;
|
||||
uint8_t address;
|
||||
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_int;
|
||||
} 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 {
|
||||
tMcp23xDevice device[MCP23XXX_MAX_DEVICES];
|
||||
uint32_t relay_inverted;
|
||||
|
@ -156,6 +176,7 @@ struct MCP230 {
|
|||
uint8_t relay_offset;
|
||||
uint8_t button_max;
|
||||
uint8_t switch_max;
|
||||
tIOCON iocon;
|
||||
int8_t button_offset;
|
||||
int8_t switch_offset;
|
||||
bool base;
|
||||
|
@ -339,15 +360,15 @@ void MCP23xPinMode(uint8_t pin, uint8_t flags) {
|
|||
}
|
||||
switch (flags) {
|
||||
case INPUT:
|
||||
MCP23xUpdate(pin, true, iodir);
|
||||
MCP23xUpdate(pin, false, gppu);
|
||||
MCP23xUpdate(pin, true, iodir); // Pin is configured as an input
|
||||
MCP23xUpdate(pin, false, gppu); // Pull-up disabled
|
||||
break;
|
||||
case INPUT_PULLUP:
|
||||
MCP23xUpdate(pin, true, iodir);
|
||||
MCP23xUpdate(pin, true, gppu);
|
||||
MCP23xUpdate(pin, true, iodir); // Pin is configured as an input
|
||||
MCP23xUpdate(pin, true, gppu); // Pull-up enabled
|
||||
break;
|
||||
case OUTPUT:
|
||||
MCP23xUpdate(pin, false, iodir);
|
||||
MCP23xUpdate(pin, false, iodir); // Pin is configured as an output
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -371,21 +392,21 @@ void MCP23xPinInterruptMode(uint8_t pin, uint8_t interrupt_mode) {
|
|||
}
|
||||
switch (interrupt_mode) {
|
||||
case MCP23XXX_CHANGE:
|
||||
MCP23xUpdate(pin, true, gpinten);
|
||||
MCP23xUpdate(pin, false, intcon);
|
||||
MCP23xUpdate(pin, true, gpinten); // Enable GPIO input pin for interrupt-on-change event
|
||||
MCP23xUpdate(pin, false, intcon); // Pin value is compared against the previous pin value
|
||||
break;
|
||||
case MCP23XXX_RISING:
|
||||
MCP23xUpdate(pin, true, gpinten);
|
||||
MCP23xUpdate(pin, true, intcon);
|
||||
MCP23xUpdate(pin, true, defval);
|
||||
MCP23xUpdate(pin, true, gpinten); // Enable GPIO input pin for interrupt-on-change event
|
||||
MCP23xUpdate(pin, true, intcon); // Controls how the associated pin value is compared for interrupt-on-change
|
||||
MCP23xUpdate(pin, false, defval); // If the associated pin level is the opposite from the register bit, an interrupt occurs.
|
||||
break;
|
||||
case MCP23XXX_FALLING:
|
||||
MCP23xUpdate(pin, true, gpinten);
|
||||
MCP23xUpdate(pin, true, intcon);
|
||||
MCP23xUpdate(pin, false, defval);
|
||||
MCP23xUpdate(pin, true, gpinten); // Enable GPIO input pin for interrupt-on-change event
|
||||
MCP23xUpdate(pin, true, intcon); // Controls how the associated pin value is compared for interrupt-on-change
|
||||
MCP23xUpdate(pin, true, defval); // If the associated pin level is the opposite from the register bit, an interrupt occurs.
|
||||
break;
|
||||
case MCP23XXX_NO_INTERRUPT:
|
||||
MCP23xUpdate(pin, false, gpinten);
|
||||
MCP23xUpdate(pin, false, gpinten); // Disable GPIO input pin for interrupt-on-change event
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -394,7 +415,7 @@ void MCP23xSetPinModes(uint8_t pin, uint8_t flags) {
|
|||
// pin 0 - 63
|
||||
MCP23xPinMode(pin, flags);
|
||||
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 mcptmplt = "";
|
||||
#ifdef USE_UFILESYS
|
||||
|
@ -497,7 +527,7 @@ bool MCP23xLoadTemplate(void) {
|
|||
}
|
||||
val = root[PSTR(D_JSON_NAME)];
|
||||
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)];
|
||||
if (arr) {
|
||||
|
@ -507,44 +537,36 @@ bool MCP23xLoadTemplate(void) {
|
|||
if (!val) { break; }
|
||||
uint16_t mpin = val.getUInt();
|
||||
if (mpin) { // Above GPIO_NONE
|
||||
if ((mpin >= AGPIO(GPIO_SWT1)) && (mpin < (AGPIO(GPIO_SWT1) + MAX_SWITCHES_SET))) {
|
||||
Mcp23x.switch_max++;
|
||||
if ((mpin >= AGPIO(GPIO_SWT1)) && (mpin < (AGPIO(GPIO_SWT1) + MAX_SWITCHES_SET)) && MCP23xAddItem(Mcp23x.switch_max)) {
|
||||
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));
|
||||
Mcp23x.switch_max++;
|
||||
MCP23xSetPinModes(pin, INPUT);
|
||||
}
|
||||
else if ((mpin >= AGPIO(GPIO_KEY1)) && (mpin < (AGPIO(GPIO_KEY1) + MAX_KEYS_SET))) {
|
||||
Mcp23x.button_max++;
|
||||
else if ((mpin >= AGPIO(GPIO_KEY1)) && (mpin < (AGPIO(GPIO_KEY1) + MAX_KEYS_SET)) && MCP23xAddItem(Mcp23x.button_max)) {
|
||||
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));
|
||||
Mcp23x.button_max++;
|
||||
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));
|
||||
mpin -= (AGPIO(GPIO_KEY1_INV) - AGPIO(GPIO_KEY1));
|
||||
Mcp23x.button_max++;
|
||||
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));
|
||||
mpin -= (AGPIO(GPIO_KEY1_INV_NP) - AGPIO(GPIO_KEY1));
|
||||
Mcp23x.button_max++;
|
||||
MCP23xSetPinModes(pin, INPUT);
|
||||
}
|
||||
else if ((mpin >= AGPIO(GPIO_REL1)) && (mpin < (AGPIO(GPIO_REL1) + MAX_RELAYS_SET))) {
|
||||
Mcp23x.relay_max++;
|
||||
else if ((mpin >= AGPIO(GPIO_REL1)) && (mpin < (AGPIO(GPIO_REL1) + MAX_RELAYS_SET)) && MCP23xAddItem(Mcp23x.relay_max)) {
|
||||
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));
|
||||
mpin -= (AGPIO(GPIO_REL1_INV) - AGPIO(GPIO_REL1));
|
||||
Mcp23x.relay_max++;
|
||||
MCP23xPinMode(pin, OUTPUT);
|
||||
}
|
||||
else if (mpin == AGPIO(GPIO_OUTPUT_HI)) {
|
||||
|
@ -558,14 +580,9 @@ bool MCP23xLoadTemplate(void) {
|
|||
else { mpin = 0; }
|
||||
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
|
||||
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);
|
||||
|
@ -582,6 +599,10 @@ uint32_t MCP23xTemplateGpio(void) {
|
|||
JsonParserObject root = parser.getRootObject();
|
||||
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)];
|
||||
if (arr.isArray()) {
|
||||
return arr.size(); // Number of requested pins
|
||||
|
@ -590,6 +611,7 @@ uint32_t MCP23xTemplateGpio(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();
|
||||
if (!pins_needed) {
|
||||
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("MCP: Invalid template"));
|
||||
|
@ -606,7 +628,8 @@ void MCP23xModuleInit(void) {
|
|||
#endif
|
||||
while ((Mcp23x.max_devices < MCP23XXX_MAX_DEVICES) && PinUsed(GPIO_MCP23SXX_CS, 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);
|
||||
digitalWrite(Mcp23x.device[Mcp23x.chip].pin_cs, 1);
|
||||
pinMode(Mcp23x.device[Mcp23x.chip].pin_cs, OUTPUT);
|
||||
|
@ -619,12 +642,14 @@ void MCP23xModuleInit(void) {
|
|||
if (0x00 == buffer) { // MCP23S08
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("SPI: MCP23S08 found at CS%d"), Mcp23x.chip +1);
|
||||
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);
|
||||
} else if (0x80 == buffer) { // MCP23S17
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("SPI: MCP23S17 found at CS%d"), Mcp23x.chip +1);
|
||||
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].olatb = MCP23xRead(MCP23X17_OLATB);
|
||||
}
|
||||
|
@ -641,8 +666,9 @@ void MCP23xModuleInit(void) {
|
|||
uint8_t mcp23xxx_address = MCP23XXX_ADDR_START;
|
||||
while ((Mcp23x.max_devices < MCP23XXX_MAX_DEVICES) && (mcp23xxx_address < MCP23XXX_ADDR_END)) {
|
||||
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)) {
|
||||
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].address = mcp23xxx_address;
|
||||
|
||||
|
@ -652,7 +678,8 @@ void MCP23xModuleInit(void) {
|
|||
if (0x00 == buffer) {
|
||||
I2cSetActiveFound(mcp23xxx_address, "MCP23008");
|
||||
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.max_devices++;
|
||||
}
|
||||
|
@ -660,7 +687,8 @@ void MCP23xModuleInit(void) {
|
|||
I2cSetActiveFound(mcp23xxx_address, "MCP23017");
|
||||
Mcp23x.device[Mcp23x.chip].pins = 16;
|
||||
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].olatb = MCP23xRead(MCP23X17_OLATB);
|
||||
Mcp23x.max_devices++;
|
||||
|
@ -691,6 +719,8 @@ void MCP23xModuleInit(void) {
|
|||
return;
|
||||
}
|
||||
|
||||
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("MCP: INT open-drain %d"), Mcp23x.iocon.ODR);
|
||||
|
||||
Mcp23x.relay_offset = TasmotaGlobal.devices_present;
|
||||
Mcp23x.relay_max -= UpdateDevicesPresent(Mcp23x.relay_max);
|
||||
|
||||
|
@ -745,8 +775,10 @@ void MCP23xInit(void) {
|
|||
} else {
|
||||
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);
|
||||
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