Sonoff Basic R4 Magic switch (#20247)

* magic switch take 1

* good to go

* good to go

* final, including in tasmota32c3
This commit is contained in:
Barbudor 2023-12-16 18:23:51 +01:00 committed by GitHub
parent 3f4b69f91c
commit 7c290388d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 229 additions and 2 deletions

View File

@ -606,6 +606,9 @@
#define USE_LIGHT_PALETTE // Add support for color palette (+0k9 code)
#define USE_LIGHT_ARTNET // Add support for DMX/ArtNet via UDP on port 6454 (+3.5k code)
#ifdef CONFIG_IDF_TARGET_ESP32C3
#define USE_MAGIC_SWITCH // Add Sonoff MagicSwitch support as implemented in Sonoff Basic R4
#endif
#define USE_DS18x20 // Add support for DS18x20 sensors with id sort, single scan and read retry (+1k3 code)

View File

@ -214,6 +214,7 @@ enum UserSelectablePins {
GPIO_HDMI_CEC, // Support for HDMI CEC
GPIO_HC8_RXD, // HC8 Serial interface
GPIO_I2S_DAC, // Audio DAC support for ESP32 and ESP32S2
GPIO_MAGIC_SWITCH, // MagicSwitch as in Sonoff BasicR4
GPIO_SENSOR_END };
// Error as warning to rethink GPIO usage with max 2045
@ -475,6 +476,7 @@ const char kSensorNames[] PROGMEM =
D_SENSOR_HDMI_CEC "|"
D_SENSOR_HC8_RX "|"
D_SENSOR_I2S_DAC "|"
D_GPIO_MAGIC_SWITCH "|"
;
const char kSensorNamesFixed[] PROGMEM =
@ -493,6 +495,7 @@ const char kSensorNamesFixed[] PROGMEM =
#define MAX_DSB 4
#define MAX_BP1658CJ_DAT 16
#define MAX_DINGTIAN_SHIFT 4
#define MAX_MAGIC_SWITCH_MODES 2
#define MAX_BL0942_RX 4 // Baudrates 1 (4800), 2 (9600), 3 (19200), 4 (38400)
const uint16_t kGpioNiceList[] PROGMEM = {
@ -1143,6 +1146,10 @@ const uint16_t kGpioNiceList[] PROGMEM = {
AGPIO(GPIO_DINGTIAN_RCK),
#endif
#ifdef USE_MAGIC_SWITCH
AGPIO(GPIO_MAGIC_SWITCH) + MAX_MAGIC_SWITCH_MODES,
#endif
/*-------------------------------------------------------------------------------------------*\
* ESP32 specifics
\*-------------------------------------------------------------------------------------------*/

View File

@ -957,6 +957,7 @@
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 Rx"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -957,6 +957,7 @@
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 Rx"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units
#define D_UNIT_AMPERE "А"

View File

@ -957,6 +957,7 @@
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 Rx"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -957,6 +957,7 @@
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 Rx"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -957,6 +957,7 @@
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 Rx"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -957,6 +957,7 @@
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 Rx"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -957,6 +957,7 @@
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 Rx"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -957,6 +957,7 @@
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 Rx"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -957,6 +957,7 @@
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 Rx"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units

View File

@ -957,6 +957,7 @@
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 Rx"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -957,6 +957,7 @@
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 Rx"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -960,6 +960,7 @@
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 Rx"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -956,7 +956,8 @@
#define D_SENSOR_BIOPDU_PZEM0XX_TX "BioPDU PZEM0XX - TX"
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 - RX"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 - RX"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -957,6 +957,7 @@
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 Rx"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -957,6 +957,7 @@
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 Rx"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -957,6 +957,7 @@
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 Rx"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -957,6 +957,7 @@
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 Rx"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -957,6 +957,7 @@
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 Rx"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -957,6 +957,7 @@
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 Rx"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -958,6 +958,7 @@
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 Rx"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units
#define D_UNIT_AMPERE "А"

View File

@ -957,6 +957,7 @@
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 Rx"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -957,6 +957,7 @@
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 Rx"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -957,6 +957,7 @@
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 Rx"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -957,6 +957,7 @@
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 Rx"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units
#define D_UNIT_AMPERE "А"

View File

@ -957,6 +957,7 @@
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 Rx"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -957,6 +957,7 @@
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 Rx"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -957,6 +957,7 @@
#define D_SENSOR_BIOPDU_PZEM016_RX "BioPDU PZEM016 Rx"
#define D_SENSOR_BIOPDU_BIT "BioPDU Bit"
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
// Units
#define D_UNIT_AMPERE "安培"

View File

@ -538,6 +538,9 @@
#define SHELLY_CMDS // Add command to send co-processor commands (+0k3 code)
#define SHELLY_FW_UPGRADE // Add firmware upgrade option for co-processor (+3k4 code)
// #define SHELLY_VOLTAGE_MON // Add support for reading voltage and current measurment (-0k0 code)
//#define USE_MAGIC_SWITCH // Add Sonoff MagicSwitch support as implemented in Sonoff Basic R4 (+612B flash, +64B IRAM for intr)
// #define MAGICSWITCH_MIN_PULSE 4000 // Overridable minimum pulse, also overridable by command MagicSwitchPulse (not saved to flash)
// #define MAGICSWITCH_MASKING_WINDOW_LEN 5 // Overridable masking window (in number of 50ms loops)
// -- Optional light modules ----------------------
#define USE_LIGHT // Add support for light control

View File

@ -0,0 +1,187 @@
/*
xdrv_91_magic_switch.ino - Driver for MagicSwitch as implemented in Sonoff BasicR4
Copyright (C) 2021 Barbudor
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef USE_MAGIC_SWITCH
#define XDRV_91 91
/********************************************************************************************************
* Check defines
*/
#ifndef MAGICSWITCH_MIN_PULSE
#define MAGICSWITCH_MIN_PULSE 4000
#endif
#ifndef MAGICSWITCH_MASKING_WINDOW_LEN
#define MAGICSWITCH_MASKING_WINDOW_LEN 5
#endif
/********************************************************************************************************
* Global private data
*/
// MagicSwitch Index => Mode
//
//
//
// Index Mode Pull-Up
// 1 0 Pull-Up
// 2 1 No Pull-up
// 3 2 Pull-Up
// 4 3 No Pull-up
// 5 4 Pull-Up
// 6 5 No Pull-up
// 7 6 Pull-Up
// 8 7 No Pull-up
#define MAGICSWITCH_MODE_NO_PULLUP 0x01
struct MAGICSWITCH_DATA {
uint32_t start_time; // timestamp at rising edge
uint32_t pulse_len; // measured pulse length
uint32_t min_pulse; // minimum pulse length
uint8_t switch_state; // switch state - count down for masking window
uint8_t pin; // the GPIO of the input
uint8_t mode; // mode
int8_t key_offset; // index of the MagicSwitch in the list of Switches
} *MagicSwitch = nullptr;
/********************************************************************************************************
* Interrupt level operations
*/
extern "C" void IRAM_ATTR MagicSwitch_intr(void *arg) {
struct MAGICSWITCH_DATA* ms = (struct MAGICSWITCH_DATA*)arg;
uint32_t now = micros();
bool pin_state = digitalRead(ms->pin);
if (ms->pulse_len) { // previous pulse not aknowledged, ignoring the edge
return;
} else if (pin_state) { // rising edge (hopefully if we didn't missed), record start time
ms->start_time = now | 1; // "| 1" => avoid 1 in 4M chance to get 0
} else if (ms->start_time) { // falling edge, compute pulse length
uint32_t pulse_len = now - ms->start_time;
ms->start_time = 0; // in case of missing an rising edge, avoid triggering false detection
if (pulse_len >= ms->min_pulse)
ms->pulse_len = pulse_len;
}
}
/********************************************************************************************************
* Driver operations
*/
void MagicSwitchLoop()
{
if (MagicSwitch->switch_state) {
MagicSwitch->switch_state--;
if (!MagicSwitch->switch_state) {
SwitchSetVirtualPinState(MagicSwitch->key_offset, 0);
MagicSwitch->pulse_len = 0; // acknowledge the pulse, close the masking window
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("MSW: end"));
}
} else if (MagicSwitch->pulse_len) {
SwitchSetVirtualPinState(MagicSwitch->key_offset, 1);
MagicSwitch->switch_state = MAGICSWITCH_MASKING_WINDOW_LEN;
AddLog(LOG_LEVEL_DEBUG, PSTR("MSW: length:%d, window:%d"), MagicSwitch->pulse_len, MagicSwitch->switch_state);
}
}
/********************************************************************************************************
* Driver initialisation
*/
void MagicSwitchInit(void) {
if (PinUsed(GPIO_MAGIC_SWITCH, GPIO_ANY)) {
// allocate data structure
MagicSwitch = (struct MAGICSWITCH_DATA*)calloc(1, sizeof(struct MAGICSWITCH_DATA));
if (MagicSwitch) {
// get pins
MagicSwitch->pin = Pin(GPIO_MAGIC_SWITCH, GPIO_ANY); // input
MagicSwitch->mode = GetPin(MagicSwitch->pin) - AGPIO(GPIO_MAGIC_SWITCH); // Index 1 => mode 0, etc...
MagicSwitch->key_offset = -1; // means not yet configured
MagicSwitch->min_pulse = MAGICSWITCH_MIN_PULSE;
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("MSW: pin:%d, mode:%d"), MagicSwitch->pin, MagicSwitch->mode);
pinMode(MagicSwitch->pin, (MagicSwitch->mode & MAGICSWITCH_MODE_NO_PULLUP) ? INPUT : INPUT_PULLUP);
attachInterruptArg(MagicSwitch->pin, MagicSwitch_intr, MagicSwitch, CHANGE);
}
}
}
bool MagicSwitchAddSwitch(void) {
if (MagicSwitch->key_offset < 0) {
MagicSwitch->key_offset = XdrvMailbox.index;
Settings->switchmode[MagicSwitch->key_offset] = 4;
AddLog(LOG_LEVEL_INFO, PSTR("MSW: Switch %d, Switchmode 4"), MagicSwitch->key_offset + 1);
return true;
}
return false;
}
/********************************************************************************************************
* Commands
*/
const char kMagicSwitchCommands[] PROGMEM = "MagicSwitch|"
"Pulse"
;
void (* const MagicSwitchCommand[])(void) PROGMEM = {
&CmndMagicSwitchPulse
};
void CmndMagicSwitchPulse(void)
{
if ((XdrvMailbox.payload >= 1000) && (XdrvMailbox.payload < 500000)) {
MagicSwitch->min_pulse = XdrvMailbox.payload;
}
ResponseCmndNumber(MagicSwitch->min_pulse);
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
bool Xdrv91(uint32_t function) {
bool result = false;
if (FUNC_SETUP_RING2 == function) {
MagicSwitchInit();
} else if (MagicSwitch) {
switch (function) {
case FUNC_EVERY_50_MSECOND:
//case FUNC_EVERY_250_MSECOND:
MagicSwitchLoop();
break;
case FUNC_ADD_SWITCH:
result = MagicSwitchAddSwitch();
break;
case FUNC_COMMAND:
result = DecodeCommand(kMagicSwitchCommands, MagicSwitchCommand);
break;
}
}
return result;
}
#endif // USE_MAGIC_SWITCH