From 5a32df5e815da485457960b1fd470728bb40ffd5 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 27 Nov 2024 13:48:30 +0100 Subject: [PATCH] Fix Shift595 output offsets and restart relay toggles --- CHANGELOG.md | 1 + RELEASENOTES.md | 1 + tasmota/tasmota_support/support.ino | 18 ++++- tasmota/tasmota_xdrv_driver/xdrv_04_light.ino | 7 ++ .../xdrv_28_pcf8574_v2.ino | 1 + .../tasmota_xdrv_driver/xdrv_60_shift595.ino | 79 +++++++++++++------ .../tasmota_xdrv_driver/xdrv_67_mcp23xxx.ino | 1 + 7 files changed, 84 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f95ca205..e66fc9fa0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ All notable changes to this project will be documented in this file. - Wrong GUI Module and Template drop down list indexes regression - Use HTML escape on File System Edit File load (#22492) - Magic switch applying masking window to any power change (#22535) +- Shift595 output offsets and restart relay toggles ### Removed diff --git a/RELEASENOTES.md b/RELEASENOTES.md index c3457d732..ca9c3883b 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -169,6 +169,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm ### Fixed - FUNC_COMMAND linked list command buffer corruption by shutter driver +- Shift595 output offsets and restart relay toggles - Use HTML escape on File System Edit File load [#22492](https://github.com/arendst/Tasmota/issues/22492) - Prevent crashing when `display.ini` is missing end `#` [#22471](https://github.com/arendst/Tasmota/issues/22471) - Magic switch applying masking window to any power change [#22535](https://github.com/arendst/Tasmota/issues/22535) diff --git a/tasmota/tasmota_support/support.ino b/tasmota/tasmota_support/support.ino index 4c96ad7aa..76f5ac099 100755 --- a/tasmota/tasmota_support/support.ino +++ b/tasmota/tasmota_support/support.ino @@ -832,12 +832,28 @@ int32_t UpdateDevicesPresent(int32_t change) { else if (devices_present >= POWER_SIZE) { // Support up to uint32_t as bitmask difference = devices_present - POWER_SIZE; devices_present = POWER_SIZE; - AddLog(LOG_LEVEL_DEBUG, PSTR("APP: Max number of devices reached")); +// AddLog(LOG_LEVEL_DEBUG, PSTR("APP: Max 32 devices supported")); } TasmotaGlobal.devices_present = devices_present; return difference; } +void DevicesPresentNonDisplayOrLight(uint32_t &devices_claimed) { + uint32_t display_and_lights = 0; +#ifdef USE_LIGHT + display_and_lights += LightDevices(); // Skip light(s) +#endif // USE_LIGHT +#ifdef USE_DISPLAY + if (disp_device) { + display_and_lights++; // Skip display + } +#endif // USE_DISPLAY + uint32_t devices_present = TasmotaGlobal.devices_present - display_and_lights; + if (devices_claimed > devices_present) { + devices_claimed = devices_present; // Reduce amount of claimed devices + } +} + char* GetPowerDevice(char* dest, uint32_t idx, size_t size, uint32_t option) { strncpy_P(dest, S_RSLT_POWER, size); // POWER diff --git a/tasmota/tasmota_xdrv_driver/xdrv_04_light.ino b/tasmota/tasmota_xdrv_driver/xdrv_04_light.ino index 047da5b43..cb8e081b4 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_04_light.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_04_light.ino @@ -293,6 +293,13 @@ uint8_t LightDevice(void) return Light.device; // Make external } +uint32_t LightDevices(void) { + if (0 == Light.device) { + return 0; + } + return TasmotaGlobal.devices_present - Light.device +1; // Make external +} + static uint32_t min3(uint32_t a, uint32_t b, uint32_t c) { return (a < b && a < c) ? a : (b < c) ? b : c; } diff --git a/tasmota/tasmota_xdrv_driver/xdrv_28_pcf8574_v2.ino b/tasmota/tasmota_xdrv_driver/xdrv_28_pcf8574_v2.ino index 874cbd772..b0f4420ce 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_28_pcf8574_v2.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_28_pcf8574_v2.ino @@ -374,6 +374,7 @@ void Pcf8574Power(void) { rpower >>= Pcf8574.relay_offset; relay_max = Pcf8574.relay_max; } + DevicesPresentNonDisplayOrLight(relay_max); // Skip display and/or light(s) for (uint32_t index = 0; index < relay_max; index++) { power_t state = rpower &1; if (Pcf8574PinUsed(GPIO_REL1, index)) { diff --git a/tasmota/tasmota_xdrv_driver/xdrv_60_shift595.ino b/tasmota/tasmota_xdrv_driver/xdrv_60_shift595.ino index 53c9a2110..c1c1280d6 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_60_shift595.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_60_shift595.ino @@ -31,15 +31,28 @@ struct Shift595 { uint8_t pinOE; uint8_t outputs; uint8_t first; - bool connected = false; } *Shift595 = nullptr; +/*********************************************************************************************\ + * Low level Shift 74x595 +\*********************************************************************************************/ + void Shift595ConfigurePin(uint8_t pin, uint8_t value = 0) { pinMode(pin, OUTPUT); digitalWrite(pin, value); } -void Shift595Init(void) { +void Shift595LatchPin(uint8_t pin) { + digitalWrite(pin, 1); + digitalWrite(pin, 0); +} + +/*********************************************************************************************\ + * FUNC_SETUP_RING2 at T +1 + * Claim devices_present +\*********************************************************************************************/ + +void Shift595ModuleInit(void) { if (PinUsed(GPIO_SHIFT595_SRCLK) && PinUsed(GPIO_SHIFT595_RCLK) && PinUsed(GPIO_SHIFT595_SER)) { Shift595 = (struct Shift595*)calloc(1, sizeof(struct Shift595)); if (Shift595) { @@ -53,40 +66,60 @@ void Shift595Init(void) { if (PinUsed(GPIO_SHIFT595_OE)) { Shift595->pinOE = Pin(GPIO_SHIFT595_OE); - Shift595ConfigurePin(Shift595->pinOE, 1); + if (ResetReasonPowerOn()) { // Fix relay toggle at restart + Shift595ConfigurePin(Shift595->pinOE, 1); // Set all outputs to 3-state (3-state converted to OFF by ULN2803 relay drivers) + } } - Shift595->first = TasmotaGlobal.devices_present; - Shift595->outputs = Settings->shift595_device_count * 8; + Shift595->first = TasmotaGlobal.devices_present; // devices_present offset + Shift595->outputs = Settings->shift595_device_count * 8; // Max number of outputs present UpdateDevicesPresent(Shift595->outputs); - - Shift595->connected = true; - AddLog(LOG_LEVEL_DEBUG, PSTR("595: Controlling relays POWER%d to POWER%d"), Shift595->first + 1, Shift595->first + Shift595->outputs); } } } -void Shift595LatchPin(uint8_t pin) { - digitalWrite(pin, 1); - digitalWrite(pin, 0); -} +/*********************************************************************************************\ + * FUNC_SET_POWER at T +2 + * Reduce devices_present with display and/or lights not known before + * Add offset for previous defined relays +\*********************************************************************************************/ void Shift595SwitchRelay(void) { - if (Shift595 && Shift595->connected == true) { - for (uint32_t i = 0; i < Shift595->outputs; i++) { - uint8_t relay_state = bitRead(XdrvMailbox.index, Shift595->first + Shift595->outputs -1 -i); - digitalWrite(Shift595->pinSER, Settings->flag5.shift595_invert_outputs ? !relay_state : relay_state); - Shift595LatchPin(Shift595->pinSRCLK); - } + // XdrvMailbox.index = 32-bit rpower bit mask + // Use relative and sequential relay indexes + power_t rpower = XdrvMailbox.index; + uint32_t relay_max = Shift595->outputs; // Total number of outputs + DevicesPresentNonDisplayOrLight(relay_max); // Skip display and/or light(s) + uint32_t relay_offset = Shift595->outputs - relay_max + Shift595->first; - Shift595LatchPin(Shift595->pinRCLK); + static bool first = false; + if (!first) { + AddLog(LOG_LEVEL_DEBUG, PSTR("595: Output 1 to %d use POWER%d to POWER%d"), Shift595->outputs - relay_offset, Shift595->first + 1, relay_max); + first = true; + } - if (PinUsed(GPIO_SHIFT595_OE)) { - digitalWrite(Shift595->pinOE, 0); + uint32_t power_bit = relay_max -1; // Start at highest non display and/or light power bit + for (uint32_t i = 0; i < Shift595->outputs; i++) { // We need to set all shift outputs even if not used + uint32_t relay_state = 0; // Unused state + if (i >= relay_offset) { + relay_state = bitRead(rpower, power_bit); // Shift-in from high to low + power_bit--; } + digitalWrite(Shift595->pinSER, Settings->flag5.shift595_invert_outputs ? !relay_state : relay_state); // SetOption133 - (Shift595) Invert outputs of 74x595 shift registers + Shift595LatchPin(Shift595->pinSRCLK); + } + + Shift595LatchPin(Shift595->pinRCLK); + + if (PinUsed(GPIO_SHIFT595_OE)) { + digitalWrite(Shift595->pinOE, 0); } } +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ + void CmndShift595Devices(void) { if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload * 8 <= MAX_RELAYS_SET - Shift595->first)) { Settings->shift595_device_count = (1 == XdrvMailbox.payload) ? SHIFT595_DEVICE_COUNT : XdrvMailbox.payload; @@ -102,8 +135,8 @@ void CmndShift595Devices(void) { bool Xdrv60(uint32_t function) { bool result = false; - if (FUNC_PRE_INIT == function) { - Shift595Init(); + if (FUNC_SETUP_RING2 == function) { + Shift595ModuleInit(); } else if (Shift595) { switch (function) { case FUNC_SET_POWER: diff --git a/tasmota/tasmota_xdrv_driver/xdrv_67_mcp23xxx.ino b/tasmota/tasmota_xdrv_driver/xdrv_67_mcp23xxx.ino index 8cb1196ac..426293236 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_67_mcp23xxx.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_67_mcp23xxx.ino @@ -762,6 +762,7 @@ void MCP23xPower(void) { rpower >>= Mcp23x.relay_offset; relay_max = Mcp23x.relay_max; } + DevicesPresentNonDisplayOrLight(relay_max); // Skip display and/or light(s) for (uint32_t index = 0; index < relay_max; index++) { power_t state = rpower &1; if (MCP23xPinUsed(GPIO_REL1, index)) {