Add Shelly Pro 4 input interrupt support

This commit is contained in:
Theo Arends 2023-01-23 12:51:02 +01:00
parent 88dd5f7f50
commit 4297fa3f3e
3 changed files with 62 additions and 49 deletions

View File

@ -89,10 +89,8 @@ void ButtonTouchFlag(uint32_t button_bit) {
void ButtonSetVirtualPinState(uint32_t index, uint32_t state) { void ButtonSetVirtualPinState(uint32_t index, uint32_t state) {
if (!Button.probe_mutex) {
bitWrite(Button.virtual_pin, index, state); bitWrite(Button.virtual_pin, index, state);
} }
}
/*********************************************************************************************/ /*********************************************************************************************/

View File

@ -68,21 +68,23 @@ void SwitchPulldownFlag(uint32 switch_bit) {
bitSet(Switch.pulldown_mask, switch_bit); bitSet(Switch.pulldown_mask, switch_bit);
} }
// Preffered virtual switch support since v12.3.1.4
void SwitchSetVirtualPinState(uint32_t index, uint32_t state) { void SwitchSetVirtualPinState(uint32_t index, uint32_t state) {
if (!Switch.probe_mutex) {
bitWrite(Switch.virtual_pin, index, state); bitWrite(Switch.virtual_pin, index, state);
} }
}
// Legacy virtual switch support
void SwitchSetVirtual(uint32_t index, uint32_t state) { void SwitchSetVirtual(uint32_t index, uint32_t state) {
bitSet(Switch.virtual_pin_used, index); // bitSet(Switch.virtual_pin_used, index);
Switch.debounced_state[index] = state; Switch.debounced_state[index] = state;
} }
// Legacy virtual switch support
uint8_t SwitchGetVirtual(uint32_t index) { uint8_t SwitchGetVirtual(uint32_t index) {
return Switch.debounced_state[index]; return Switch.debounced_state[index];
} }
// Legacy virtual switch support
uint8_t SwitchLastState(uint32_t index) { uint8_t SwitchLastState(uint32_t index) {
return Switch.last_state[index]; return Switch.last_state[index];
} }

View File

@ -45,8 +45,9 @@
struct SPro { struct SPro {
uint32_t last_update; uint32_t last_update;
uint32_t probe_pin; uint32_t probe_pin;
int switch_offset; uint16_t input_state;
int button_offset; int8_t switch_offset;
int8_t button_offset;
uint8_t pin_register_cs; uint8_t pin_register_cs;
uint8_t pin_mcp23s17_int; uint8_t pin_mcp23s17_int;
uint8_t ledlink; uint8_t ledlink;
@ -98,33 +99,32 @@ void SP4Mcp23S17Disable(void) {
digitalWrite(SPro.pin_register_cs, 1); digitalWrite(SPro.pin_register_cs, 1);
} }
uint32_t SP4Mcp23S17ReadGpio(void) { uint32_t SP4Mcp23S17Read16(uint8_t reg) {
// Read 16-bit gpio registers: (gpiob << 8) | gpioa // Read 16-bit registers: (regb << 8) | rega
SP4Mcp23S17Enable();
SPI.transfer(SHELLY_PRO_4_MCP23S17_ADDRESS | 1);
SPI.transfer(SP4_MCP23S17_GPIOA);
uint32_t gpio = SPI.transfer(0xFF); // SP4_MCP23S17_GPIOA
gpio |= (SPI.transfer(0xFF) << 8); // SP4_MCP23S17_GPIOB
SP4Mcp23S17Disable();
return gpio;
}
bool SP4Mcp23S17Read(uint8_t reg, uint8_t *value) {
SP4Mcp23S17Enable(); SP4Mcp23S17Enable();
SPI.transfer(SHELLY_PRO_4_MCP23S17_ADDRESS | 1); SPI.transfer(SHELLY_PRO_4_MCP23S17_ADDRESS | 1);
SPI.transfer(reg); SPI.transfer(reg);
*value = SPI.transfer(0xFF); uint32_t value = SPI.transfer(0xFF); // RegA
value |= (SPI.transfer(0xFF) << 8); // RegB
SP4Mcp23S17Disable(); SP4Mcp23S17Disable();
return true; return value;
} }
bool SP4Mcp23S17Write(uint8_t reg, uint8_t value) { uint32_t SP4Mcp23S17Read(uint8_t reg) {
SP4Mcp23S17Enable();
SPI.transfer(SHELLY_PRO_4_MCP23S17_ADDRESS | 1);
SPI.transfer(reg);
uint32_t value = SPI.transfer(0xFF);
SP4Mcp23S17Disable();
return value;
}
void SP4Mcp23S17Write(uint8_t reg, uint8_t value) {
SP4Mcp23S17Enable(); SP4Mcp23S17Enable();
SPI.transfer(SHELLY_PRO_4_MCP23S17_ADDRESS); SPI.transfer(SHELLY_PRO_4_MCP23S17_ADDRESS);
SPI.transfer(reg); SPI.transfer(reg);
SPI.transfer(value); SPI.transfer(value);
SP4Mcp23S17Disable(); SP4Mcp23S17Disable();
return true;
} }
void SP4Mcp23S17Update(uint8_t pin, bool pin_value, uint8_t reg_addr) { void SP4Mcp23S17Update(uint8_t pin, bool pin_value, uint8_t reg_addr) {
@ -135,7 +135,7 @@ void SP4Mcp23S17Update(uint8_t pin, bool pin_value, uint8_t reg_addr) {
} else if (reg_addr == SP4_MCP23S17_OLATB) { } else if (reg_addr == SP4_MCP23S17_OLATB) {
reg_value = sp4_mcp23s17_olatb; reg_value = sp4_mcp23s17_olatb;
} else { } else {
SP4Mcp23S17Read(reg_addr, &reg_value); reg_value = SP4Mcp23S17Read(reg_addr);
} }
if (pin_value) { if (pin_value) {
reg_value |= 1 << bit; reg_value |= 1 << bit;
@ -167,8 +167,7 @@ void SP4Mcp23S17PinMode(uint8_t pin, uint8_t flags) {
bool SP4Mcp23S17DigitalRead(uint8_t pin) { bool SP4Mcp23S17DigitalRead(uint8_t pin) {
uint8_t bit = pin % 8; uint8_t bit = pin % 8;
uint8_t reg_addr = pin < 8 ? SP4_MCP23S17_GPIOA : SP4_MCP23S17_GPIOB; uint8_t reg_addr = pin < 8 ? SP4_MCP23S17_GPIOA : SP4_MCP23S17_GPIOB;
uint8_t value = 0; uint8_t value = SP4Mcp23S17Read(reg_addr);
SP4Mcp23S17Read(reg_addr, &value);
return value & (1 << bit); return value & (1 << bit);
} }
@ -210,8 +209,8 @@ void ShellyPro4Init(void) {
SP4Mcp23S17Write(SP4_MCP23S17_GPINTENB, 0x80); // Enable interrupt on change SP4Mcp23S17Write(SP4_MCP23S17_GPINTENB, 0x80); // Enable interrupt on change
// Read current output register state // Read current output register state
SP4Mcp23S17Read(SP4_MCP23S17_OLATA, &sp4_mcp23s17_olata); sp4_mcp23s17_olata = SP4Mcp23S17Read(SP4_MCP23S17_OLATA);
SP4Mcp23S17Read(SP4_MCP23S17_OLATB, &sp4_mcp23s17_olatb); sp4_mcp23s17_olatb = SP4Mcp23S17Read(SP4_MCP23S17_OLATB);
for (uint32_t i = 0; i < 4; i++) { for (uint32_t i = 0; i < 4; i++) {
SP4Mcp23S17PinMode(sp4_switch_pin[i], INPUT); // Switch1..4 SP4Mcp23S17PinMode(sp4_switch_pin[i], INPUT); // Switch1..4
@ -226,6 +225,8 @@ void ShellyPro4Init(void) {
SP4Mcp23S17PinMode(4, OUTPUT); // Reset display, ADE7943 SP4Mcp23S17PinMode(4, OUTPUT); // Reset display, ADE7943
SP4Mcp23S17DigitalWrite(4, 1); SP4Mcp23S17DigitalWrite(4, 1);
attachInterrupt(SPro.pin_mcp23s17_int, ShellyProUpdateIsr, CHANGE);
} }
void ShellyPro4Reset(void) { void ShellyPro4Reset(void) {
@ -239,7 +240,9 @@ bool ShellyProAddButton(void) {
if (SPro.button_offset < 0) { SPro.button_offset = XdrvMailbox.index; } if (SPro.button_offset < 0) { SPro.button_offset = XdrvMailbox.index; }
uint32_t index = XdrvMailbox.index - SPro.button_offset; uint32_t index = XdrvMailbox.index - SPro.button_offset;
if (index > 2) { return false; } // Support three buttons if (index > 2) { return false; } // Support three buttons
XdrvMailbox.index = SP4Mcp23S17DigitalRead(sp4_button_pin[index]); uint32_t state = SP4Mcp23S17DigitalRead(sp4_button_pin[index]);
bitWrite(SPro.input_state, sp4_button_pin[index], state);
XdrvMailbox.index = state;
return true; return true;
} }
@ -248,29 +251,42 @@ bool ShellyProAddSwitch(void) {
if (SPro.switch_offset < 0) { SPro.switch_offset = XdrvMailbox.index; } if (SPro.switch_offset < 0) { SPro.switch_offset = XdrvMailbox.index; }
uint32_t index = XdrvMailbox.index - SPro.switch_offset; uint32_t index = XdrvMailbox.index - SPro.switch_offset;
if (index > 3) { return false; } // Support four switches if (index > 3) { return false; } // Support four switches
XdrvMailbox.index = SP4Mcp23S17DigitalRead(sp4_switch_pin[index]); uint32_t state = SP4Mcp23S17DigitalRead(sp4_switch_pin[index]);
bitWrite(SPro.input_state, sp4_switch_pin[index], state);
XdrvMailbox.index = state ^1; // Invert
return true; return true;
} }
void ShellyProUpdateSwitches(void) { void ShellyProUpdateIsr(void) {
if (SPro.detected != 4) { return; } // Only support Shelly Pro 4 /*
if (digitalRead(SPro.pin_mcp23s17_int)) { return; } // Poll interrupt The goal if this function is to minimize SPI and SetVirtualPinState calls
*/
uint32_t gpio = SP4Mcp23S17ReadGpio(); // Read gpio and clear interrupt uint32_t input_state = SP4Mcp23S17Read16(SP4_MCP23S17_INTCAPA); // Read intcap and clear interrupt
input_state &= 0x806F; // Only test input bits
// AddLog(LOG_LEVEL_DEBUG, PSTR("SHP: Input detected 0x%04X"), gpio); // AddLog(LOG_LEVEL_DEBUG, PSTR("SHP: Input detected %04X, was %04X"), input_state, SPro.input_state);
uint32_t mask = 1;
for (uint32_t j = 0; j < 16; j++) {
if ((input_state & mask) != (SPro.input_state & mask)) {
uint32_t state = (input_state >> j) &1;
// AddLog(LOG_LEVEL_DEBUG, PSTR("SHP: Change pin %d to %d"), j, state);
// Propagate state
uint32_t state;
for (uint32_t i = 0; i < 4; i++) { for (uint32_t i = 0; i < 4; i++) {
state = (gpio >> sp4_switch_pin[i]) &1; if (j == sp4_switch_pin[i]) {
SwitchSetVirtualPinState(SPro.switch_offset +i, state); SwitchSetVirtualPinState(SPro.switch_offset +i, state ^1); // Invert
} }
for (uint32_t i = 0; i < 3; i++) { else if ((i < 3) && (j == sp4_button_pin[i])) {
state = (gpio >> sp4_button_pin[i]) &1;
ButtonSetVirtualPinState(SPro.button_offset +i, state); ButtonSetVirtualPinState(SPro.button_offset +i, state);
} }
} }
}
mask <<= 1;
}
SPro.input_state = input_state;
}
bool ShellyProButton(void) { bool ShellyProButton(void) {
if (SPro.detected != 4) { return false; } // Only support Shelly Pro 4 if (SPro.detected != 4) { return false; } // Only support Shelly Pro 4
@ -458,9 +474,6 @@ bool Xdrv88(uint32_t function) {
ShellyProPreInit(); ShellyProPreInit();
} else if (SPro.detected) { } else if (SPro.detected) {
switch (function) { switch (function) {
case FUNC_EVERY_50_MSECOND:
ShellyProUpdateSwitches();
break;
/* /*
case FUNC_BUTTON_PRESSED: case FUNC_BUTTON_PRESSED:
result = ShellyProButton(); result = ShellyProButton();