Fix Shift595 output offsets and restart relay toggles

This commit is contained in:
Theo Arends 2024-11-27 13:48:30 +01:00
parent 5dd132bb5f
commit 5a32df5e81
7 changed files with 84 additions and 24 deletions

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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;
}

View File

@ -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)) {

View File

@ -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:

View File

@ -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)) {