Update changelogs

- Add MCP23008 virtual switch/button/relay support
This commit is contained in:
Theo Arends 2023-03-03 10:28:22 +01:00
parent edb2fad6c1
commit d07d4b06c9
4 changed files with 111 additions and 49 deletions

View File

@ -12,6 +12,8 @@ All notable changes to this project will be documented in this file.
### Changed
### Fixed
- TuyaMcu v1 sequence fix (#17625)
- TuyaMcu v1 timer integer overflow (#18048)
### Removed

View File

@ -126,8 +126,10 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm
- Move #define OTA_URL from user_config.h to board files for better inital support [#18008](https://github.com/arendst/Tasmota/issues/18008)
### Fixed
- TuyaMcu v1 sequence fix [#17625](https://github.com/arendst/Tasmota/issues/17625)
- SEN5X floats and units [#17961](https://github.com/arendst/Tasmota/issues/17961)
- Energytotals cannot be set to negative values [#17965](https://github.com/arendst/Tasmota/issues/17965)
- SR04 driver single pin ultrasonic sensor detection [#17966](https://github.com/arendst/Tasmota/issues/17966)
- IR panasonic protocol regression from v12.0.2.4 [#18013](https://github.com/arendst/Tasmota/issues/18013)
- EnergyTotal divided twice during minimal upgrade step regression from v12.3.1.3 [#18024](https://github.com/arendst/Tasmota/issues/18024)
- TuyaMcu v1 timer integer overflow [#18048](https://github.com/arendst/Tasmota/issues/18048)

View File

@ -20,7 +20,6 @@
* and handle any input and output as configured GPIOs.
*
* Restrictions:
* - Supports MCP23017 (=I2C) and MCP23S17 (=SPI)
* - Uses incremental I2C addresses / SPI Chip select until template pin count reached
* - Max support for 28 switches (input), 32 buttons (input), 32 relays (output)
*
@ -38,14 +37,16 @@
* Switch_n1..28 Sn 192..219 Switch to Gnd without internal pullup
* Relay1..28 R 224..255 Relay
* Relay_i1..28 Ri 256..287 Relay inverted
* Output_Hi Oh 3840 Fixed output high
* Output_lo Ol 3872 Fixed output low
*
* Prepare a template to be loaded either by:
* - a rule like: rule3 on file#mcp23x.dat do {"NAME":"MCP23017 A=Ri8-1, B=B1-8","GPIO":[263,262,261,260,259,258,257,256,32,33,34,35,36,37,38,39]} endon
* - a script like: -y{"NAME":"MCP23017 A=Ri8-1, B=B1-8","GPIO":[263,262,261,260,259,258,257,256,32,33,34,35,36,37,38,39]}
* - file called mcp23x.dat with contents: {"NAME":"MCP23017 A=Ri8-1, B=B1-8","GPIO":[263,262,261,260,259,258,257,256,32,33,34,35,36,37,38,39]}
*
* S3 S2 B2 B3 B1 S1 R1 R4 R2 R3 S4
* {"NAME":"MCP23S17 Shelly Pro 4PM","GPIO":[194,193,65,66,0,64,192,0,224,0,0,0,227,225,226,195]}
* S3 S2 B2 B3 Oh B1 S1 R1 R4 R2 R3 S4
* {"NAME":"MCP23S17 Shelly Pro 4PM","GPIO":[194,193,65,66,3840,64,192,0,224,0,0,0,227,225,226,195]}
*
* Inverted relays and buttons Ri8 Ri7 Ri6 Ri5 Ri4 Ri3 Ri2 Ri1 B1 B2 B3 B4 B5 B6 B7 B8
* {"NAME":"MCP23017 A=Ri8-1, B=B1-8","GPIO":[263,262,261,260,259,258,257,256,32,33,34,35,36,37,38,39]}
@ -71,13 +72,15 @@
\*********************************************************************************************/
#define XDRV_67 67
#define XI2C_77 77 // See I2CDEVICES.md
#define XI2C_77 77 // See I2CDEVICES.md
#define MCP23XXX_ADDR_START 0x20 // 32
#define MCP23XXX_ADDR_END 0x26 // 38
#define MCP23XXX_ADDR_START 0x20 // 32
#define MCP23XXX_ADDR_END 0x26 // 38
#define MCP23XXX_MAX_DEVICES 6
#define MCP23XXX_SPI_CLOCK 1000000 // SPI clock speed set to 1MHz in case of signal interference at higher speed (Max is 10MHz)
/*********************************************************************************************\
* MCP23017 support
\*********************************************************************************************/
@ -133,7 +136,6 @@ typedef struct {
uint8_t address;
uint8_t interface;
uint8_t pins; // 8 (MCP23008) or 16 (MCP23017 / MCP23S17)
uint8_t type; // 1 (MCP23008) or 2 (MCP23017) or 3 (MCP23S17)
int8_t pin_cs;
int8_t pin_int;
} tMcp23xDevice;
@ -162,7 +164,7 @@ uint16_t *Mcp23x_gpio_pin = nullptr;
#ifdef USE_SPI
void MCP23xEnable(void) {
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
SPI.beginTransaction(SPISettings(MCP23XXX_SPI_CLOCK, MSBFIRST, SPI_MODE0));
digitalWrite(Mcp23x.device[Mcp23x.chip].pin_cs, 0);
}
@ -175,12 +177,14 @@ void MCP23xDisable(void) {
void MCP23xDumpRegs(void) {
uint8_t data[22];
for (Mcp23x.chip = 0; Mcp23x.chip < Mcp23x.max_devices; Mcp23x.chip++) {
uint32_t data_size = sizeof(data);
if (8 == Mcp23x.device[Mcp23x.chip].pins) { data_size /= 2; }
#ifdef USE_SPI
if (MCP23X_SPI == Mcp23x.device[Mcp23x.chip].interface) {
MCP23xEnable();
SPI.transfer(Mcp23x.device[Mcp23x.chip].address | 1);
SPI.transfer(0);
for (uint32_t i = 0; i < sizeof(data); i++) {
for (uint32_t i = 0; i < data_size; i++) {
data[i] = SPI.transfer(0xFF);
}
MCP23xDisable();
@ -188,10 +192,10 @@ void MCP23xDumpRegs(void) {
#endif
#ifdef USE_I2C
if (MCP23X_I2C == Mcp23x.device[Mcp23x.chip].interface) {
I2cReadBuffer(Mcp23x.device[Mcp23x.chip].address, 0, data, sizeof(data));
I2cReadBuffer(Mcp23x.device[Mcp23x.chip].address, 0, data, data_size);
}
#endif
AddLog(LOG_LEVEL_DEBUG, PSTR("MCP: Intf %d, Address %02X, Regs %*_H"), Mcp23x.device[Mcp23x.chip].interface, Mcp23x.device[Mcp23x.chip].address, sizeof(data), data);
AddLog(LOG_LEVEL_DEBUG, PSTR("MCP: Intf %d, Address %02X, Regs %*_H"), Mcp23x.device[Mcp23x.chip].interface, Mcp23x.device[Mcp23x.chip].address, data_size, data);
}
}
@ -279,6 +283,9 @@ void MCP23xUpdate(uint8_t pin, bool pin_value, uint8_t reg_addr) {
uint8_t reg_value = 0;
if (reg_addr == MCP23X17_OLATA) {
reg_value = Mcp23x.device[Mcp23x.chip].olata;
if (8 == Mcp23x.device[Mcp23x.chip].pins) {
reg_addr = MCP23X08_OLAT;
}
} else if (reg_addr == MCP23X17_OLATB) {
reg_value = Mcp23x.device[Mcp23x.chip].olatb;
} else {
@ -290,6 +297,9 @@ void MCP23xUpdate(uint8_t pin, bool pin_value, uint8_t reg_addr) {
reg_value &= ~(1 << bit);
}
MCP23xWrite(reg_addr, reg_value);
if ((8 == Mcp23x.device[Mcp23x.chip].pins) && (reg_addr == MCP23X08_OLAT)) {
reg_addr = MCP23X17_OLATA;
}
if (reg_addr == MCP23X17_OLATA) {
Mcp23x.device[Mcp23x.chip].olata = reg_value;
} else if (reg_addr == MCP23X17_OLATB) {
@ -299,12 +309,28 @@ void MCP23xUpdate(uint8_t pin, bool pin_value, uint8_t reg_addr) {
/*********************************************************************************************/
uint32_t MCP23xSetChip(uint8_t pin) {
// Calculate chip based on number of pins per chip. 8 for MCP23008, 16 for MCP23x17
// pin 0 - 63
for (Mcp23x.chip = 0; Mcp23x.chip < Mcp23x.max_devices; Mcp23x.chip++) {
if (Mcp23x.device[Mcp23x.chip].pins > pin) { break; }
pin -= Mcp23x.device[Mcp23x.chip].pins;
}
return pin; // relative pin number within chip (0 ... 7 or 0 ... 15)
}
void MCP23xPinMode(uint8_t pin, uint8_t flags) {
// pin 0 - 31
Mcp23x.chip = pin / 16;
pin = pin % 16;
uint8_t iodir = pin < 8 ? MCP23X17_IODIRA : MCP23X17_IODIRB;
uint8_t gppu = pin < 8 ? MCP23X17_GPPUA : MCP23X17_GPPUB;
// pin 0 - 63
pin = MCP23xSetChip(pin);
uint8_t iodir;
uint8_t gppu;
if (8 == Mcp23x.device[Mcp23x.chip].pins) {
iodir = MCP23X08_IODIR;
gppu = MCP23X08_GPPU;
} else {
iodir = pin < 8 ? MCP23X17_IODIRA : MCP23X17_IODIRB;
gppu = pin < 8 ? MCP23X17_GPPUA : MCP23X17_GPPUB;
}
switch (flags) {
case INPUT:
MCP23xUpdate(pin, true, iodir);
@ -318,15 +344,25 @@ void MCP23xPinMode(uint8_t pin, uint8_t flags) {
MCP23xUpdate(pin, false, iodir);
break;
}
// AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: MCP23xPinMode chip %d, pin %d, flags %d, regs %d,%d"), Mcp23x.chip, pin, flags, iodir, gppu);
}
void MCP23xPinInterruptMode(uint8_t pin, uint8_t interrupt_mode) {
// pin 0 - 31
Mcp23x.chip = pin / 16;
pin = pin % 16;
uint8_t gpinten = pin < 8 ? MCP23X17_GPINTENA : MCP23X17_GPINTENB;
uint8_t intcon = pin < 8 ? MCP23X17_INTCONA : MCP23X17_INTCONB;
uint8_t defval = pin < 8 ? MCP23X17_DEFVALA : MCP23X17_DEFVALB;
// pin 0 - 63
pin = MCP23xSetChip(pin);
uint8_t gpinten;
uint8_t intcon;
uint8_t defval;
if (8 == Mcp23x.device[Mcp23x.chip].pins) {
gpinten = MCP23X08_GPINTEN;
intcon = MCP23X08_INTCON;
defval = MCP23X08_DEFVAL;
} else {
gpinten = pin < 8 ? MCP23X17_GPINTENA : MCP23X17_GPINTENB;
intcon = pin < 8 ? MCP23X17_INTCONA : MCP23X17_INTCONB;
defval = pin < 8 ? MCP23X17_DEFVALA : MCP23X17_DEFVALB;
}
switch (interrupt_mode) {
case MCP23XXX_CHANGE:
MCP23xUpdate(pin, true, gpinten);
@ -348,21 +384,35 @@ void MCP23xPinInterruptMode(uint8_t pin, uint8_t interrupt_mode) {
}
}
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);
}
}
bool MCP23xDigitalRead(uint8_t pin) {
// pin 0 - 31
Mcp23x.chip = pin / 16;
pin = pin % 16;
// pin 0 - 63
pin = MCP23xSetChip(pin);
uint8_t bit = pin % 8;
uint8_t reg_addr = pin < 8 ? MCP23X17_GPIOA : MCP23X17_GPIOB;
uint8_t reg_addr;
if (8 == Mcp23x.device[Mcp23x.chip].pins) {
reg_addr = MCP23X08_GPIO;
} else {
reg_addr = pin < 8 ? MCP23X17_GPIOA : MCP23X17_GPIOB;
}
uint8_t value = MCP23xRead(reg_addr);
return value & (1 << bit);
}
void MCP23xDigitalWrite(uint8_t pin, bool value) {
// pin 0 - 31
Mcp23x.chip = pin / 16;
pin = pin % 16;
// pin 0 - 63
pin = MCP23xSetChip(pin);
uint8_t reg_addr = pin < 8 ? MCP23X17_OLATA : MCP23X17_OLATB;
// AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: MCP23xDigitalWrite chip %d, pin %d, state %d, reg %d"), Mcp23x.chip, pin, value, reg_addr);
MCP23xUpdate(pin, value, reg_addr);
}
@ -370,8 +420,8 @@ void MCP23xDigitalWrite(uint8_t pin, bool value) {
* Tasmota
\*********************************************************************************************/
int IRAM_ATTR MCP23xPin(uint32_t gpio, uint32_t index = 0);
int IRAM_ATTR MCP23xPin(uint32_t gpio, uint32_t index) {
int MCP23xPin(uint32_t gpio, uint32_t index = 0);
int MCP23xPin(uint32_t gpio, uint32_t index) {
uint16_t real_gpio = gpio << 5;
uint16_t mask = 0xFFE0;
if (index < GPIO_ANY) {
@ -419,13 +469,6 @@ String MCP23xTemplateLoadFile(void) {
return mcptmplt;
}
void MCP23xSetPinModes(uint8_t pin, uint8_t flags) {
MCP23xPinMode(pin, flags);
if (Mcp23x.device[Mcp23x.chip].pin_int > -1) { // Mcp23x.chip is updated by call to MCP23xPinMode
MCP23xPinInterruptMode(pin, MCP23XXX_CHANGE);
}
}
bool MCP23xLoadTemplate(void) {
String mcptmplt = MCP23xTemplateLoadFile();
uint32_t len = mcptmplt.length() +1;
@ -494,6 +537,14 @@ bool MCP23xLoadTemplate(void) {
Mcp23x.relay_max++;
MCP23xPinMode(pin, OUTPUT);
}
else if (mpin == AGPIO(GPIO_OUTPUT_HI)) {
MCP23xPinMode(pin, OUTPUT);
MCP23xDigitalWrite(pin, true);
}
else if (mpin == AGPIO(GPIO_OUTPUT_LO)) {
MCP23xPinMode(pin, OUTPUT);
MCP23xDigitalWrite(pin, false);
}
else { mpin = 0; }
Mcp23x_gpio_pin[pin] = mpin;
}
@ -552,7 +603,6 @@ void MCP23xModuleInit(void) {
Mcp23x.device[Mcp23x.chip].interface = MCP23X_SPI;
Mcp23x.device[Mcp23x.chip].address = MCP23XXX_ADDR_START << 1;
AddLog(LOG_LEVEL_INFO, PSTR("SPI: MCP23S17 found at CS%d"), Mcp23x.chip +1);
Mcp23x.device[Mcp23x.chip].type = 3;
Mcp23x.device[Mcp23x.chip].pins = 16;
MCP23xWrite(MCP23X17_IOCONA, 0b01011000); // Enable INT mirror, Slew rate disabled, HAEN pins for addressing
Mcp23x.device[Mcp23x.chip].olata = MCP23xRead(MCP23X17_OLATA);
@ -578,16 +628,14 @@ void MCP23xModuleInit(void) {
uint8_t buffer;
if (MCP23xValidRead(MCP23X08_IOCON, &buffer)) {
if (0x00 == buffer) {
/*
I2cSetActiveFound(mcp23xxx_address, "MCP23008");
Mcp23x.device[Mcp23x.chip].type = 1;
Mcp23x.device[Mcp23x.chip].pins = 8;
MCP23xWrite(MCP23X08_IOCON, 0b00011000); // Slew rate disabled, HAEN pins for addressing
Mcp23x.device[Mcp23x.chip].olata = MCP23xRead(MCP23X08_OLAT);
Mcp23x.max_devices++;
*/
}
else if (0x80 == buffer) {
I2cSetActiveFound(mcp23xxx_address, "MCP23017");
Mcp23x.device[Mcp23x.chip].type = 2;
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
@ -630,11 +678,16 @@ void MCP23xModuleInit(void) {
void MCP23xServiceInput(void) {
// I found no reliable way to receive interrupts; noise received at undefined moments - unstable usage
Mcp23x.interrupt = 0;
Mcp23x.interrupt = false;
// This works with no interrupt
uint32_t pin_offset = 0;
uint32_t gpio;
for (Mcp23x.chip = 0; Mcp23x.chip < Mcp23x.max_devices; Mcp23x.chip++) {
uint32_t gpio = MCP23xRead16(MCP23X17_GPIOA); // Read gpio
if (8 == Mcp23x.device[Mcp23x.chip].pins) {
gpio = MCP23xRead(MCP23X08_GPIO); // Read MCP23008 gpio
} else {
gpio = MCP23xRead16(MCP23X17_GPIOA); // Read MCP23x17 gpio
}
// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("MCP: Chip %d, State %04X"), Mcp23x.chip, gpio);
@ -657,14 +710,19 @@ void MCP23xServiceInput(void) {
}
void IRAM_ATTR MCP23xInputIsr(void) {
Mcp23x.interrupt = 1;
Mcp23x.interrupt = true;
}
void MCP23xInit(void) {
if (Mcp23x.button_max || Mcp23x.switch_max) {
uint32_t gpio;
for (Mcp23x.chip = 0; Mcp23x.chip < Mcp23x.max_devices; Mcp23x.chip++) {
if (Mcp23x.device[Mcp23x.chip].pin_int > -1) {
uint32_t gpio = MCP23xRead16(MCP23X17_GPIOA); // Clear interrupt
if (8 == Mcp23x.device[Mcp23x.chip].pins) {
gpio = MCP23xRead(MCP23X08_GPIO); // Clear MCP23008 interrupt
} else {
gpio = MCP23xRead16(MCP23X17_GPIOA); // Clear MCP23x17 interrupt
}
attachInterrupt(Mcp23x.device[Mcp23x.chip].pin_int, MCP23xInputIsr, CHANGE);
}
}

View File

@ -28,7 +28,7 @@
* {"NAME":"Shelly Pro 1PM","GPIO":[9568,1,9472,1,768,0,0,0,672,704,736,0,0,0,5600,6214,0,0,0,5568,0,0,0,0,0,0,0,0,3459,0,0,32,4736,0,160,0],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,5600,4700,3350"}
* {"NAME":"Shelly Pro 2","GPIO":[0,1,0,1,768,0,0,0,672,704,736,0,0,0,5600,6214,0,0,0,5568,0,0,0,0,0,0,0,0,0,0,0,32,4736,4737,160,161],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,5600,4700,3350;AdcParam2 2,5600,4700,3350"}
* {"NAME":"Shelly Pro 2PM","GPIO":[9568,1,9472,1,768,0,0,0,672,704,736,9569,0,0,5600,6214,0,0,0,5568,0,0,0,0,0,0,0,0,3460,0,0,32,4736,4737,160,161],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,5600,4700,3350;AdcParam2 2,5600,4700,3350"}
* {"NAME":"Shelly Pro 4PM","GPIO":[0,6210,0,6214,9568,0,0,0,0,0,9569,0,10272,0,5600,0,0,0,0,5568,0,0,0,0,0,0,0,0,736,704,3461,10240,4736,0,0,672],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,5600,4700,3350;rule3 on file#mcp23x.dat do {\"NAME\":\"MCP23S17 Shelly Pro 4PM\",\"GPIO\":[194,193,65,66,0,64,192,0,224,0,0,0,227,225,226,195]} endon"}
* {"NAME":"Shelly Pro 4PM","GPIO":[0,6210,0,6214,9568,0,0,0,0,0,9569,0,10272,0,5600,0,0,0,0,5568,0,0,0,0,0,0,0,0,736,704,3461,10240,4736,0,0,672],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,5600,4700,3350;rule3 on file#mcp23x.dat do {\"NAME\":\"MCP23S17 Shelly Pro 4PM\",\"GPIO\":[194,193,65,66,3840,64,192,0,224,0,0,0,227,225,226,195]} endon"}
*
* Shelly Pro 1/2 uses SPI to control one 74HC595 for relays/leds and one ADE7953 (1PM) or two ADE7953 (2PM) for energy monitoring
* Shelly Pro 4 uses an SPI to control one MCP23S17 for buttons/switches/relays/leds and two ADE7953 for energy monitoring and a second SPI for the display
@ -78,7 +78,7 @@ void ShellyPro4Init(void) {
}
void ShellyPro4Reset(void) {
MCP23xPinMode(4, OUTPUT);
// MCP23xPinMode(4, OUTPUT); // Performed by MCP23x.dat template as Output_Hi
MCP23xDigitalWrite(4, 0); // Reset pin display, ADE7953
delay(1); // To initiate a hardware reset, this pin must be brought low for a minimum of 10 μs.
MCP23xDigitalWrite(4, 1);