2019-12-02 09:44:27 +00:00
/*
support_tasmota . ino - Core support for Tasmota
2021-01-01 12:44:04 +00:00
Copyright ( C ) 2021 Theo Arends
2019-12-02 09:44:27 +00:00
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/>.
*/
2022-01-16 20:34:29 +00:00
# if defined(ESP32) && defined(USE_WEBCLIENT_HTTPS)
# include "HttpClientLight.h"
# endif
2019-12-02 09:44:27 +00:00
const char kSleepMode [ ] PROGMEM = " Dynamic|Normal " ;
const char kPrefixes [ ] PROGMEM = D_CMND " | " D_STAT " | " D_TELE ;
2021-01-18 20:48:04 +00:00
char * Format ( char * output , const char * input_p , int size )
2019-12-02 09:44:27 +00:00
{
char * token ;
uint32_t digits = 0 ;
2021-01-18 20:48:04 +00:00
char input [ strlen_P ( input_p ) + 1 ] ; // copy from PMEM to RAM
strcpy_P ( input , input_p ) ;
2019-12-02 09:44:27 +00:00
2020-11-04 10:20:17 +00:00
if ( strchr ( input , ' % ' ) ! = nullptr ) {
2019-12-02 09:44:27 +00:00
strlcpy ( output , input , size ) ;
token = strtok ( output , " % " ) ;
2020-11-04 10:20:17 +00:00
if ( strchr ( input , ' % ' ) = = input ) {
2019-12-02 09:44:27 +00:00
output [ 0 ] = ' \0 ' ;
} else {
token = strtok ( nullptr , " " ) ;
}
if ( token ! = nullptr ) {
digits = atoi ( token ) ;
if ( digits ) {
char tmp [ size ] ;
if ( strchr ( token , ' d ' ) ) {
snprintf_P ( tmp , size , PSTR ( " %s%c0%dd " ) , output , ' % ' , digits ) ;
2020-04-13 16:45:06 +01:00
snprintf_P ( output , size , tmp , ESP_getChipId ( ) & 0x1fff ) ; // %04d - short chip ID in dec, like in hostname
2019-12-02 09:44:27 +00:00
} else {
2021-04-11 12:29:33 +01:00
String mac_address = NetworkUniqueId ( ) ;
2020-05-26 16:08:13 +01:00
if ( digits > 12 ) { digits = 12 ; }
String mac_part = mac_address . substring ( 12 - digits ) ;
snprintf_P ( output , size , PSTR ( " %s%s " ) , output , mac_part . c_str ( ) ) ; // %01X .. %12X - mac address in hex
2019-12-02 09:44:27 +00:00
}
} else {
if ( strchr ( token , ' d ' ) ) {
2020-05-26 16:08:13 +01:00
snprintf_P ( output , size , PSTR ( " %s%d " ) , output , ESP_getChipId ( ) ) ; // %d - full chip ID in dec
2019-12-02 09:44:27 +00:00
digits = 8 ;
}
}
}
}
if ( ! digits ) {
strlcpy ( output , input , size ) ;
}
return output ;
}
char * GetOtaUrl ( char * otaurl , size_t otaurl_size )
{
2019-12-16 14:13:57 +00:00
if ( strstr ( SettingsText ( SET_OTAURL ) , " %04d " ) ! = nullptr ) { // OTA url contains placeholder for chip ID
2020-04-13 16:45:06 +01:00
snprintf ( otaurl , otaurl_size , SettingsText ( SET_OTAURL ) , ESP_getChipId ( ) & 0x1fff ) ;
2019-12-02 09:44:27 +00:00
}
2019-12-16 14:13:57 +00:00
else if ( strstr ( SettingsText ( SET_OTAURL ) , " %d " ) ! = nullptr ) { // OTA url contains placeholder for chip ID
2020-04-13 16:45:06 +01:00
snprintf_P ( otaurl , otaurl_size , SettingsText ( SET_OTAURL ) , ESP_getChipId ( ) ) ;
2019-12-02 09:44:27 +00:00
}
else {
2019-12-16 14:13:57 +00:00
strlcpy ( otaurl , SettingsText ( SET_OTAURL ) , otaurl_size ) ;
2019-12-02 09:44:27 +00:00
}
2019-12-16 14:13:57 +00:00
2019-12-02 09:44:27 +00:00
return otaurl ;
}
2021-07-21 10:41:31 +01:00
String ResolveToken ( const char * input ) {
String resolved = input ;
resolved . replace ( F ( " %hostname% " ) , TasmotaGlobal . hostname ) ;
resolved . replace ( F ( " %id% " ) , NetworkUniqueId ( ) ) ;
return resolved ;
}
2022-04-16 18:43:08 +01:00
char * GetTopic_P ( char * stopic , uint32_t prefix , const char * topic , const char * subtopic )
2019-12-02 09:44:27 +00:00
{
/* prefix 0 = Cmnd
prefix 1 = Stat
prefix 2 = Tele
prefix 4 = Cmnd fallback
prefix 5 = Stat fallback
prefix 6 = Tele fallback
prefix 8 = Cmnd topic
prefix 9 = Stat topic
prefix 10 = Tele topic
*/
char romram [ CMDSZ ] ;
String fulltopic ;
snprintf_P ( romram , sizeof ( romram ) , subtopic ) ;
2020-10-30 11:29:48 +00:00
if ( TasmotaGlobal . fallback_topic_flag | | ( prefix > 3 ) ) {
2019-12-02 09:44:27 +00:00
bool fallback = ( prefix < 8 ) ;
prefix & = 3 ;
char stemp [ 11 ] ;
fulltopic = GetTextIndexed ( stemp , sizeof ( stemp ) , prefix , kPrefixes ) ;
fulltopic + = F ( " / " ) ;
if ( fallback ) {
2020-10-30 11:29:48 +00:00
fulltopic + = TasmotaGlobal . mqtt_client ;
2019-12-02 09:44:27 +00:00
fulltopic + = F ( " _fb " ) ; // cmnd/<mqttclient>_fb
} else {
2021-01-18 20:48:04 +00:00
fulltopic + = ( const __FlashStringHelper * ) topic ; // cmnd/<grouptopic>
2019-12-02 09:44:27 +00:00
}
} else {
2019-12-16 14:13:57 +00:00
fulltopic = SettingsText ( SET_MQTT_FULLTOPIC ) ;
2019-12-02 09:44:27 +00:00
if ( ( 0 = = prefix ) & & ( - 1 = = fulltopic . indexOf ( FPSTR ( MQTT_TOKEN_PREFIX ) ) ) ) {
fulltopic + = F ( " / " ) ;
fulltopic + = FPSTR ( MQTT_TOKEN_PREFIX ) ; // Need prefix for commands to handle mqtt topic loops
}
2020-01-12 12:10:21 +00:00
for ( uint32_t i = 0 ; i < MAX_MQTT_PREFIXES ; i + + ) {
2019-12-21 16:57:54 +00:00
if ( ! strlen ( SettingsText ( SET_MQTTPREFIX1 + i ) ) ) {
2019-12-16 14:13:57 +00:00
char temp [ TOPSZ ] ;
SettingsUpdateText ( SET_MQTTPREFIX1 + i , GetTextIndexed ( temp , sizeof ( temp ) , i , kPrefixes ) ) ;
2019-12-02 09:44:27 +00:00
}
}
2019-12-16 14:13:57 +00:00
fulltopic . replace ( FPSTR ( MQTT_TOKEN_PREFIX ) , SettingsText ( SET_MQTTPREFIX1 + prefix ) ) ;
2021-01-18 20:48:04 +00:00
fulltopic . replace ( FPSTR ( MQTT_TOKEN_TOPIC ) , ( const __FlashStringHelper * ) topic ) ;
2021-07-21 10:41:31 +01:00
fulltopic = ResolveToken ( fulltopic . c_str ( ) ) ;
2019-12-02 09:44:27 +00:00
}
fulltopic . replace ( F ( " # " ) , " " ) ;
fulltopic . replace ( F ( " // " ) , " / " ) ;
if ( ! fulltopic . endsWith ( " / " ) ) {
fulltopic + = " / " ;
}
snprintf_P ( stopic , TOPSZ , PSTR ( " %s%s " ) , fulltopic . c_str ( ) , romram ) ;
return stopic ;
}
2020-03-28 15:48:36 +00:00
char * GetGroupTopic_P ( char * stopic , const char * subtopic , uint32_t itopic )
2019-12-02 09:44:27 +00:00
{
// SetOption75 0: %prefix%/nothing/%topic% = cmnd/nothing/<grouptopic>/#
// SetOption75 1: cmnd/<grouptopic>
2021-06-11 17:14:12 +01:00
return GetTopic_P ( stopic , ( Settings - > flag3 . grouptopic_mode ) ? CMND + 8 : CMND , SettingsText ( itopic ) , subtopic ) ; // SetOption75 - GroupTopic replaces %topic% (0) or fixed topic cmnd/grouptopic (1)
2019-12-02 09:44:27 +00:00
}
char * GetFallbackTopic_P ( char * stopic , const char * subtopic )
{
return GetTopic_P ( stopic , CMND + 4 , nullptr , subtopic ) ;
}
char * GetStateText ( uint32_t state )
{
2020-01-12 12:10:21 +00:00
if ( state > = MAX_STATE_TEXT ) {
2019-12-02 09:44:27 +00:00
state = 1 ;
}
2019-12-16 14:13:57 +00:00
return SettingsText ( SET_STATE_TXT1 + state ) ;
2019-12-02 09:44:27 +00:00
}
2021-03-24 16:13:54 +00:00
/*********************************************************************************************\
* Zero - cross support
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
//#define DEBUG_ZEROCROSS
2019-12-02 09:44:27 +00:00
2021-03-23 10:21:38 +00:00
void ZeroCrossMomentStart ( void ) {
2021-03-24 16:13:54 +00:00
if ( ! TasmotaGlobal . zc_interval ) { return ; }
2021-03-23 10:21:38 +00:00
2021-03-24 16:13:54 +00:00
# ifdef DEBUG_ZEROCROSS
uint32_t dbg_interval = TasmotaGlobal . zc_interval ;
uint32_t dbg_zctime = TasmotaGlobal . zc_time ;
uint32_t dbg_starttime = micros ( ) ;
# endif
2021-03-23 10:21:38 +00:00
uint32_t trigger_moment = TasmotaGlobal . zc_time + TasmotaGlobal . zc_interval - TasmotaGlobal . zc_offset - TasmotaGlobal . zc_code_offset ;
2021-03-25 08:37:44 +00:00
while ( TimeReachedUsec ( trigger_moment ) ) { // Trigger moment already passed so try next
2021-03-24 16:13:54 +00:00
trigger_moment + = TasmotaGlobal . zc_interval ;
}
2021-03-25 08:37:44 +00:00
while ( ! TimeReachedUsec ( trigger_moment ) ) { } // Wait for trigger moment
2021-03-23 10:21:38 +00:00
2021-03-24 16:13:54 +00:00
# ifdef DEBUG_ZEROCROSS
uint32_t dbg_endtime = micros ( ) ;
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " ZCD: CodeExecTime %d, StartTime %u, EndTime %u, ZcTime %u, Interval %d " ) ,
dbg_endtime - dbg_starttime , dbg_starttime , dbg_endtime , dbg_zctime , dbg_interval ) ;
# endif
2021-03-23 10:21:38 +00:00
TasmotaGlobal . zc_code_offset = micros ( ) ;
}
void ZeroCrossMomentEnd ( void ) {
2021-03-24 16:13:54 +00:00
if ( ! TasmotaGlobal . zc_interval ) { return ; }
2021-03-23 10:21:38 +00:00
TasmotaGlobal . zc_code_offset = ( micros ( ) - TasmotaGlobal . zc_code_offset ) / 2 ;
2021-03-24 16:13:54 +00:00
# ifdef DEBUG_ZEROCROSS
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " ZCD: CodeExecTime %d " ) , TasmotaGlobal . zc_code_offset * 2 ) ;
# endif
2021-03-23 10:21:38 +00:00
}
2021-04-02 16:14:08 +01:00
void IRAM_ATTR ZeroCrossIsr ( void ) {
2021-03-23 10:21:38 +00:00
uint32_t time = micros ( ) ;
2021-03-24 16:13:54 +00:00
TasmotaGlobal . zc_interval = ( ( int32_t ) ( time - TasmotaGlobal . zc_time ) ) ;
2021-03-23 10:21:38 +00:00
TasmotaGlobal . zc_time = time ;
}
2021-03-23 14:42:15 +00:00
void ZeroCrossInit ( uint32_t offset ) {
if ( PinUsed ( GPIO_ZEROCROSS ) ) {
TasmotaGlobal . zc_offset = offset ;
uint32_t gpio = Pin ( GPIO_ZEROCROSS ) ;
pinMode ( gpio , INPUT_PULLUP ) ;
attachInterrupt ( gpio , ZeroCrossIsr , CHANGE ) ;
AddLog ( LOG_LEVEL_INFO , PSTR ( " ZCD: Activated " ) ) ; // Zero-cross detection activated
}
2021-03-23 10:21:38 +00:00
}
/********************************************************************************************/
2019-12-02 09:44:27 +00:00
void SetLatchingRelay ( power_t lpower , uint32_t state )
{
2020-10-28 18:03:39 +00:00
// TasmotaGlobal.power xx00 - toggle REL1 (Off) and REL3 (Off) - device 1 Off, device 2 Off
// TasmotaGlobal.power xx01 - toggle REL2 (On) and REL3 (Off) - device 1 On, device 2 Off
// TasmotaGlobal.power xx10 - toggle REL1 (Off) and REL4 (On) - device 1 Off, device 2 On
// TasmotaGlobal.power xx11 - toggle REL2 (On) and REL4 (On) - device 1 On, device 2 On
2020-10-28 11:40:52 +00:00
static power_t latching_power = 0 ; // Power state at latching start
2019-12-02 09:44:27 +00:00
2020-10-29 12:58:50 +00:00
if ( state & & ! TasmotaGlobal . latching_relay_pulse ) { // Set latching relay to power if previous pulse has finished
2019-12-02 09:44:27 +00:00
latching_power = lpower ;
2020-10-29 12:58:50 +00:00
TasmotaGlobal . latching_relay_pulse = 2 ; // max 200mS (initiated by stateloop())
2019-12-02 09:44:27 +00:00
}
2020-10-30 11:29:48 +00:00
for ( uint32_t i = 0 ; i < TasmotaGlobal . devices_present ; i + + ) {
2019-12-02 09:44:27 +00:00
uint32_t port = ( i < < 1 ) + ( ( latching_power > > i ) & 1 ) ;
2020-10-28 18:03:39 +00:00
DigitalWrite ( GPIO_REL1 , port , bitRead ( TasmotaGlobal . rel_inverted , port ) ? ! state : state ) ;
2019-12-02 09:44:27 +00:00
}
}
void SetDevicePower ( power_t rpower , uint32_t source )
{
ShowSource ( source ) ;
2020-10-30 11:29:48 +00:00
TasmotaGlobal . last_source = source ;
2019-12-02 09:44:27 +00:00
2021-06-11 17:14:12 +01:00
if ( POWER_ALL_ALWAYS_ON = = Settings - > poweronstate ) { // All on and stay on
2022-02-18 15:13:55 +00:00
TasmotaGlobal . power = POWER_MASK > > ( POWER_SIZE - TasmotaGlobal . devices_present ) ;
2020-10-28 18:03:39 +00:00
rpower = TasmotaGlobal . power ;
2019-12-02 09:44:27 +00:00
}
2021-06-11 17:14:12 +01:00
if ( Settings - > flag . interlock ) { // Allow only one or no relay set - CMND_INTERLOCK - Enable/disable interlock
2019-12-02 09:44:27 +00:00
for ( uint32_t i = 0 ; i < MAX_INTERLOCKS ; i + + ) {
power_t mask = 1 ;
uint32_t count = 0 ;
2020-10-30 11:29:48 +00:00
for ( uint32_t j = 0 ; j < TasmotaGlobal . devices_present ; j + + ) {
2021-06-11 17:14:12 +01:00
if ( ( Settings - > interlock [ i ] & mask ) & & ( rpower & mask ) ) {
2019-12-02 09:44:27 +00:00
count + + ;
}
mask < < = 1 ;
}
if ( count > 1 ) {
2021-06-11 17:14:12 +01:00
mask = ~ Settings - > interlock [ i ] ; // Turn interlocked group off as there would be multiple relays on
2020-10-28 18:03:39 +00:00
TasmotaGlobal . power & = mask ;
2019-12-02 09:44:27 +00:00
rpower & = mask ;
}
}
}
if ( rpower ) { // Any power set
2020-10-28 18:03:39 +00:00
TasmotaGlobal . last_power = rpower ;
2019-12-02 09:44:27 +00:00
}
XdrvMailbox . index = rpower ;
XdrvCall ( FUNC_SET_POWER ) ; // Signal power state
2020-08-03 17:08:49 +01:00
XsnsCall ( FUNC_SET_POWER ) ; // Signal power state
2019-12-02 09:44:27 +00:00
XdrvMailbox . index = rpower ;
XdrvMailbox . payload = source ;
if ( XdrvCall ( FUNC_SET_DEVICE_POWER ) ) { // Set power state and stop if serviced
// Serviced
}
2020-04-10 17:24:08 +01:00
# ifdef ESP8266
2020-10-30 11:29:48 +00:00
else if ( ( SONOFF_DUAL = = TasmotaGlobal . module_type ) | | ( CH4 = = TasmotaGlobal . module_type ) ) {
2019-12-02 09:44:27 +00:00
Serial . write ( 0xA0 ) ;
Serial . write ( 0x04 ) ;
Serial . write ( rpower & 0xFF ) ;
Serial . write ( 0xA1 ) ;
Serial . write ( ' \n ' ) ;
Serial . flush ( ) ;
}
2020-10-30 11:29:48 +00:00
else if ( EXS_RELAY = = TasmotaGlobal . module_type ) {
2019-12-02 09:44:27 +00:00
SetLatchingRelay ( rpower , 1 ) ;
}
2020-04-10 17:24:08 +01:00
# endif // ESP8266
2020-05-26 17:20:46 +01:00
else
2020-04-10 17:24:08 +01:00
{
2021-03-23 10:21:38 +00:00
ZeroCrossMomentStart ( ) ;
2020-10-30 11:29:48 +00:00
for ( uint32_t i = 0 ; i < TasmotaGlobal . devices_present ; i + + ) {
2019-12-02 09:44:27 +00:00
power_t state = rpower & 1 ;
2019-12-18 17:21:10 +00:00
if ( i < MAX_RELAYS ) {
2020-10-28 18:03:39 +00:00
DigitalWrite ( GPIO_REL1 , i , bitRead ( TasmotaGlobal . rel_inverted , i ) ? ! state : state ) ;
2019-12-02 09:44:27 +00:00
}
rpower > > = 1 ;
}
2021-03-23 10:21:38 +00:00
ZeroCrossMomentEnd ( ) ;
2019-12-02 09:44:27 +00:00
}
}
void RestorePower ( bool publish_power , uint32_t source )
{
2020-10-28 18:03:39 +00:00
if ( TasmotaGlobal . power ! = TasmotaGlobal . last_power ) {
TasmotaGlobal . power = TasmotaGlobal . last_power ;
SetDevicePower ( TasmotaGlobal . power , source ) ;
2019-12-02 09:44:27 +00:00
if ( publish_power ) {
MqttPublishAllPowerState ( ) ;
}
}
}
void SetAllPower ( uint32_t state , uint32_t source )
{
// state 0 = POWER_OFF = Relay Off
2021-06-11 17:14:12 +01:00
// state 1 = POWER_ON = Relay On (turn off after Settings->pulse_timer * 100 mSec if enabled)
2019-12-02 09:44:27 +00:00
// state 2 = POWER_TOGGLE = Toggle relay
// state 8 = POWER_OFF_NO_STATE = Relay Off and no publishPowerState
// state 9 = POWER_ON_NO_STATE = Relay On and no publishPowerState
// state 10 = POWER_TOGGLE_NO_STATE = Toggle relay and no publishPowerState
// state 16 = POWER_SHOW_STATE = Show power state
bool publish_power = true ;
if ( ( state > = POWER_OFF_NO_STATE ) & & ( state < = POWER_TOGGLE_NO_STATE ) ) {
state & = 3 ; // POWER_OFF, POWER_ON or POWER_TOGGLE
publish_power = false ;
}
if ( ( state > = POWER_OFF ) & & ( state < = POWER_TOGGLE ) ) {
2022-02-18 15:13:55 +00:00
power_t all_on = POWER_MASK > > ( POWER_SIZE - TasmotaGlobal . devices_present ) ;
2019-12-02 09:44:27 +00:00
switch ( state ) {
case POWER_OFF :
2020-10-28 18:03:39 +00:00
TasmotaGlobal . power = 0 ;
2019-12-02 09:44:27 +00:00
break ;
case POWER_ON :
2020-10-28 18:03:39 +00:00
TasmotaGlobal . power = all_on ;
2019-12-02 09:44:27 +00:00
break ;
case POWER_TOGGLE :
2020-10-29 11:58:22 +00:00
TasmotaGlobal . power ^ = all_on ; // Complement current state
2019-12-02 09:44:27 +00:00
}
2020-10-28 18:03:39 +00:00
SetDevicePower ( TasmotaGlobal . power , source ) ;
2019-12-02 09:44:27 +00:00
}
if ( publish_power ) {
MqttPublishAllPowerState ( ) ;
}
}
2020-01-11 14:39:56 +00:00
void SetPowerOnState ( void )
{
2020-04-10 17:24:08 +01:00
# ifdef ESP8266
2020-10-30 11:29:48 +00:00
if ( MOTOR = = TasmotaGlobal . module_type ) {
2021-06-11 17:14:12 +01:00
Settings - > poweronstate = POWER_ALL_ON ; // Needs always on else in limbo!
2020-01-11 14:39:56 +00:00
}
2020-04-10 17:24:08 +01:00
# endif // ESP8266
2021-06-11 17:14:12 +01:00
if ( POWER_ALL_ALWAYS_ON = = Settings - > poweronstate ) {
2020-01-11 14:39:56 +00:00
SetDevicePower ( 1 , SRC_RESTART ) ;
} else {
2022-02-18 15:13:55 +00:00
power_t devices_mask = POWER_MASK > > ( POWER_SIZE - TasmotaGlobal . devices_present ) ;
2020-01-11 14:39:56 +00:00
if ( ( ResetReason ( ) = = REASON_DEFAULT_RST ) | | ( ResetReason ( ) = = REASON_EXT_SYS_RST ) ) {
2021-06-11 17:14:12 +01:00
switch ( Settings - > poweronstate ) {
2020-01-11 14:39:56 +00:00
case POWER_ALL_OFF :
case POWER_ALL_OFF_PULSETIME_ON :
2020-10-28 18:03:39 +00:00
TasmotaGlobal . power = 0 ;
SetDevicePower ( TasmotaGlobal . power , SRC_RESTART ) ;
2020-01-11 14:39:56 +00:00
break ;
case POWER_ALL_ON : // All on
2022-02-18 15:13:55 +00:00
TasmotaGlobal . power = devices_mask ;
2020-10-28 18:03:39 +00:00
SetDevicePower ( TasmotaGlobal . power , SRC_RESTART ) ;
2020-01-11 14:39:56 +00:00
break ;
case POWER_ALL_SAVED_TOGGLE :
2022-02-18 15:13:55 +00:00
TasmotaGlobal . power = ( Settings - > power & devices_mask ) ^ POWER_MASK ;
2021-06-11 17:14:12 +01:00
if ( Settings - > flag . save_state ) { // SetOption0 - Save power state and use after restart
2020-10-28 18:03:39 +00:00
SetDevicePower ( TasmotaGlobal . power , SRC_RESTART ) ;
2020-01-11 14:39:56 +00:00
}
break ;
case POWER_ALL_SAVED :
2022-02-18 15:13:55 +00:00
TasmotaGlobal . power = Settings - > power & devices_mask ;
2021-06-11 17:14:12 +01:00
if ( Settings - > flag . save_state ) { // SetOption0 - Save power state and use after restart
2020-10-28 18:03:39 +00:00
SetDevicePower ( TasmotaGlobal . power , SRC_RESTART ) ;
2020-01-11 14:39:56 +00:00
}
break ;
}
} else {
2022-02-18 15:13:55 +00:00
TasmotaGlobal . power = Settings - > power & devices_mask ;
2021-06-11 17:14:12 +01:00
if ( Settings - > flag . save_state ) { // SetOption0 - Save power state and use after restart
2020-10-28 18:03:39 +00:00
SetDevicePower ( TasmotaGlobal . power , SRC_RESTART ) ;
2020-01-11 14:39:56 +00:00
}
}
}
// Issue #526 and #909
2020-10-30 11:29:48 +00:00
for ( uint32_t i = 0 ; i < TasmotaGlobal . devices_present ; i + + ) {
2022-03-14 15:07:35 +00:00
# ifdef ESP8266
2021-06-11 17:14:12 +01:00
if ( ! Settings - > flag3 . no_power_feedback ) { // SetOption63 - Don't scan relay power state at restart - #5594 and #5663
2020-04-27 11:54:07 +01:00
if ( ( i < MAX_RELAYS ) & & PinUsed ( GPIO_REL1 , i ) ) {
2020-10-28 18:03:39 +00:00
bitWrite ( TasmotaGlobal . power , i , digitalRead ( Pin ( GPIO_REL1 , i ) ) ^ bitRead ( TasmotaGlobal . rel_inverted , i ) ) ;
2020-01-11 14:39:56 +00:00
}
}
2022-03-14 15:07:35 +00:00
# endif // ESP8266
2021-06-11 17:14:12 +01:00
if ( bitRead ( TasmotaGlobal . power , i ) | | ( POWER_ALL_OFF_PULSETIME_ON = = Settings - > poweronstate ) ) {
SetPulseTimer ( i % MAX_PULSETIMERS , Settings - > pulse_timer [ i % MAX_PULSETIMERS ] ) ;
2020-01-11 14:39:56 +00:00
}
}
2020-10-28 18:03:39 +00:00
TasmotaGlobal . blink_powersave = TasmotaGlobal . power ;
2022-02-17 19:01:03 +00:00
# ifdef USE_RULES
2022-02-17 17:21:09 +00:00
RulesEvery50ms ( ) ;
2022-02-17 17:39:46 +00:00
# endif
2020-01-11 14:39:56 +00:00
}
More sensible behaviour
* When setting PWM values, updates all the LEDs (instant response). Uses led_power values.
* If LEDLINK not set, but LED1 is, LED1 is the status led. When turning on/off, setledlink uses digitalwrite (which does not respect the new pwm operation). In this case only, we will use the setledpoweridx instead of digitalwrite - costly (every 250ms this runs), but edge case / legacy. Allows more intuitive operation - if we blink an LED with the max and min PWM limits, we'd expect it to respect these. In this case, blink will also now update the led_power status, which keeps this accurate e.g. if ledpower 1 cmnd was sent, then blink occurred, led_state would read a 1 for that bit but the led would be off (but nothing was reading it for status so it didn't cause any trouble). Leaving digitalwrite when LEDLINK is defined as this is more efficient and the use case for pwm leds is to find buttons - link indicator would become more ambiguous for no benefit.
2020-05-20 02:25:32 +01:00
void UpdateLedPowerAll ( )
{
2020-10-30 11:29:48 +00:00
for ( uint32_t i = 0 ; i < TasmotaGlobal . leds_present ; i + + ) {
SetLedPowerIdx ( i , bitRead ( TasmotaGlobal . led_power , i ) ) ;
More sensible behaviour
* When setting PWM values, updates all the LEDs (instant response). Uses led_power values.
* If LEDLINK not set, but LED1 is, LED1 is the status led. When turning on/off, setledlink uses digitalwrite (which does not respect the new pwm operation). In this case only, we will use the setledpoweridx instead of digitalwrite - costly (every 250ms this runs), but edge case / legacy. Allows more intuitive operation - if we blink an LED with the max and min PWM limits, we'd expect it to respect these. In this case, blink will also now update the led_power status, which keeps this accurate e.g. if ledpower 1 cmnd was sent, then blink occurred, led_state would read a 1 for that bit but the led would be off (but nothing was reading it for status so it didn't cause any trouble). Leaving digitalwrite when LEDLINK is defined as this is more efficient and the use case for pwm leds is to find buttons - link indicator would become more ambiguous for no benefit.
2020-05-20 02:25:32 +01:00
}
}
2019-12-02 09:44:27 +00:00
void SetLedPowerIdx ( uint32_t led , uint32_t state )
{
2021-10-16 12:26:38 +01:00
/*
// Fix legacy led support 20211016 (Notice: legacy led supports TWO leds max)
2020-04-27 16:16:52 +01:00
if ( ! PinUsed ( GPIO_LEDLNK ) & & ( 0 = = led ) ) { // Legacy - LED1 is link led only if LED2 is present
2020-04-27 11:54:07 +01:00
if ( PinUsed ( GPIO_LED1 , 1 ) ) {
2019-12-02 09:44:27 +00:00
led = 1 ;
}
}
2021-10-16 12:26:38 +01:00
*/
2020-04-27 11:54:07 +01:00
if ( PinUsed ( GPIO_LED1 , led ) ) {
2019-12-02 09:44:27 +00:00
uint32_t mask = 1 < < led ;
if ( state ) {
state = 1 ;
2020-10-30 11:29:48 +00:00
TasmotaGlobal . led_power | = mask ;
2019-12-02 09:44:27 +00:00
} else {
2020-10-30 11:29:48 +00:00
TasmotaGlobal . led_power & = ( 0xFF ^ mask ) ;
2019-12-02 09:44:27 +00:00
}
2020-05-23 12:09:16 +01:00
uint16_t pwm = 0 ;
2021-06-11 17:14:12 +01:00
if ( bitRead ( Settings - > ledpwm_mask , led ) ) {
2020-05-23 12:09:16 +01:00
# ifdef USE_LIGHT
2021-06-11 17:14:12 +01:00
pwm = changeUIntScale ( ledGamma10 ( state ? Settings - > ledpwm_on : Settings - > ledpwm_off ) , 0 , 1023 , 0 , Settings - > pwm_range ) ; // gamma corrected
2020-05-23 12:09:16 +01:00
# else //USE_LIGHT
2021-06-11 17:14:12 +01:00
pwm = changeUIntScale ( ( uint16_t ) ( state ? Settings - > ledpwm_on : Settings - > ledpwm_off ) , 0 , 255 , 0 , Settings - > pwm_range ) ; // linear
2020-05-23 12:09:16 +01:00
# endif //USE_LIGHT
2021-12-22 15:50:29 +00:00
# ifdef ESP32
2022-01-27 20:30:05 +00:00
if ( analogAttach ( Pin ( GPIO_LED1 , led ) ) > = 0 )
2021-12-22 15:50:29 +00:00
# endif
analogWrite ( Pin ( GPIO_LED1 , led ) , bitRead ( TasmotaGlobal . led_inverted , led ) ? Settings - > pwm_range - pwm : pwm ) ;
2020-05-23 12:09:16 +01:00
} else {
2020-10-30 11:29:48 +00:00
DigitalWrite ( GPIO_LED1 , led , bitRead ( TasmotaGlobal . led_inverted , led ) ? ! state : state ) ;
2020-05-23 12:09:16 +01:00
}
2019-12-02 09:44:27 +00:00
}
2020-02-06 15:25:37 +00:00
# ifdef USE_BUZZER
if ( led = = 0 ) {
BuzzerSetStateToLed ( state ) ;
}
# endif // USE_BUZZER
2019-12-02 09:44:27 +00:00
}
2021-10-16 12:26:38 +01:00
void SetLedPower ( bool state )
2019-12-02 09:44:27 +00:00
{
2020-04-27 16:16:52 +01:00
if ( ! PinUsed ( GPIO_LEDLNK ) ) { // Legacy - Only use LED1 and/or LED2
2021-10-16 12:26:38 +01:00
/*
2019-12-02 09:44:27 +00:00
SetLedPowerIdx ( 0 , state ) ;
2021-10-16 12:26:38 +01:00
*/
// Fix legacy led support 20211016 (Notice: legacy led supports TWO leds max)
uint32_t led = ( PinUsed ( GPIO_LED1 , 1 ) ) ? 1 : 0 ;
SetLedPowerIdx ( led , state ) ;
2019-12-02 09:44:27 +00:00
} else {
power_t mask = 1 ;
2020-10-30 11:29:48 +00:00
for ( uint32_t i = 0 ; i < TasmotaGlobal . leds_present ; i + + ) { // Map leds to power
2020-10-28 18:03:39 +00:00
bool tstate = ( TasmotaGlobal . power & mask ) ;
2019-12-02 09:44:27 +00:00
SetLedPowerIdx ( i , tstate ) ;
mask < < = 1 ;
}
}
}
void SetLedPowerAll ( uint32_t state )
{
2020-10-30 11:29:48 +00:00
for ( uint32_t i = 0 ; i < TasmotaGlobal . leds_present ; i + + ) {
2019-12-02 09:44:27 +00:00
SetLedPowerIdx ( i , state ) ;
}
}
void SetLedLink ( uint32_t state )
{
2021-02-14 12:06:19 +00:00
int led_pin = Pin ( GPIO_LEDLNK ) ;
2020-10-30 11:29:48 +00:00
uint32_t led_inv = TasmotaGlobal . ledlnk_inverted ;
2021-02-14 12:06:19 +00:00
if ( - 1 = = led_pin ) { // Legacy - LED1 is status
2020-05-23 12:09:16 +01:00
SetLedPowerIdx ( 0 , state ) ;
}
2021-02-15 16:26:55 +00:00
else if ( led_pin > = 0 ) {
2019-12-02 09:44:27 +00:00
if ( state ) { state = 1 ; }
digitalWrite ( led_pin , ( led_inv ) ? ! state : state ) ;
}
2020-02-06 15:25:37 +00:00
# ifdef USE_BUZZER
2020-01-28 10:22:36 +00:00
BuzzerSetStateToLed ( state ) ;
2020-02-06 15:25:37 +00:00
# endif // USE_BUZZER
2021-11-29 20:53:24 +00:00
# ifdef USE_PWM_DIMMER
if ( Settings - > flag4 . powered_off_led ) TasmotaGlobal . restore_powered_off_led_counter = 3 ;
# endif // USE_PWM_DIMMER
2019-12-02 09:44:27 +00:00
}
void SetPulseTimer ( uint32_t index , uint32_t time )
{
2020-10-28 16:32:07 +00:00
TasmotaGlobal . pulse_timer [ index ] = ( time > 111 ) ? millis ( ) + ( 1000 * ( time - 100 ) ) : ( time > 0 ) ? millis ( ) + ( 100 * time ) : 0L ;
2019-12-02 09:44:27 +00:00
}
uint32_t GetPulseTimer ( uint32_t index )
{
2020-10-28 16:32:07 +00:00
long time = TimePassedSince ( TasmotaGlobal . pulse_timer [ index ] ) ;
2019-12-02 09:44:27 +00:00
if ( time < 0 ) {
time * = - 1 ;
return ( time > 11100 ) ? ( time / 1000 ) + 100 : ( time > 0 ) ? time / 100 : 0 ;
}
return 0 ;
}
/********************************************************************************************/
bool SendKey ( uint32_t key , uint32_t device , uint32_t state )
{
// key 0 = KEY_BUTTON = button_topic
// key 1 = KEY_SWITCH = switch_topic
// state 0 = POWER_OFF = off
// state 1 = POWER_ON = on
// state 2 = POWER_TOGGLE = toggle
// state 3 = POWER_HOLD = hold
2020-05-19 14:52:10 +01:00
// state 4 = POWER_INCREMENT = button still pressed
// state 5 = POWER_INV = button released
// state 6 = POWER_CLEAR = button released
// state 7 = POWER_RELEASE = button released
2019-12-02 09:44:27 +00:00
// state 9 = CLEAR_RETAIN = clear retain flag
2021-08-29 22:03:14 +01:00
// state 10 = POWER_DELAYED = button released delayed
2019-12-02 09:44:27 +00:00
char stopic [ TOPSZ ] ;
char scommand [ CMDSZ ] ;
2019-12-16 14:13:57 +00:00
char key_topic [ TOPSZ ] ;
2019-12-02 09:44:27 +00:00
bool result = false ;
2020-03-04 17:02:27 +00:00
uint32_t device_save = device ;
2019-12-02 09:44:27 +00:00
2019-12-16 14:13:57 +00:00
char * tmp = ( key ) ? SettingsText ( SET_MQTT_SWITCH_TOPIC ) : SettingsText ( SET_MQTT_BUTTON_TOPIC ) ;
2019-12-02 09:44:27 +00:00
Format ( key_topic , tmp , sizeof ( key_topic ) ) ;
2021-06-11 17:14:12 +01:00
if ( Settings - > flag . mqtt_enabled & & MqttIsConnected ( ) & & ( strlen ( key_topic ) ! = 0 ) & & strcmp ( key_topic , " 0 " ) ) { // SetOption3 - Enable MQTT
2020-10-30 11:29:48 +00:00
if ( ! key & & ( device > TasmotaGlobal . devices_present ) ) {
2019-12-02 09:44:27 +00:00
device = 1 ; // Only allow number of buttons up to number of devices
}
GetTopic_P ( stopic , CMND , key_topic ,
2021-06-11 17:14:12 +01:00
GetPowerDevice ( scommand , device , sizeof ( scommand ) , ( key + Settings - > flag . device_index_enable ) ) ) ; // cmnd/switchtopic/POWERx - SetOption26 - Switch between POWER or POWER1
2019-12-02 09:44:27 +00:00
if ( CLEAR_RETAIN = = state ) {
2020-10-30 11:29:48 +00:00
ResponseClear ( ) ;
2019-12-02 09:44:27 +00:00
} else {
2021-06-11 17:14:12 +01:00
if ( ( Settings - > flag3 . button_switch_force_local | | // SetOption61 - Force local operation when button/switch topic is set
2020-10-30 11:29:48 +00:00
! strcmp ( TasmotaGlobal . mqtt_topic , key_topic ) | |
2019-12-16 14:13:57 +00:00
! strcmp ( SettingsText ( SET_MQTT_GRP_TOPIC ) , key_topic ) ) & &
2019-12-02 09:44:27 +00:00
( POWER_TOGGLE = = state ) ) {
2020-10-29 11:58:22 +00:00
state = ~ ( TasmotaGlobal . power > > ( device - 1 ) ) & 1 ; // POWER_OFF or POWER_ON
2019-12-02 09:44:27 +00:00
}
2021-05-23 13:42:27 +01:00
Response_P ( GetStateText ( state ) ) ;
2019-12-02 09:44:27 +00:00
}
# ifdef USE_DOMOTICZ
2021-05-23 13:42:27 +01:00
if ( ! ( DomoticzSendKey ( key , device , state , ResponseLength ( ) ) ) ) {
2019-12-02 09:44:27 +00:00
# endif // USE_DOMOTICZ
2021-06-11 17:14:12 +01:00
MqttPublish ( stopic , ( ( key ) ? Settings - > flag . mqtt_switch_retain // CMND_SWITCHRETAIN
: Settings - > flag . mqtt_button_retain ) & & // CMND_BUTTONRETAIN
( state ! = POWER_HOLD | | ! Settings - > flag3 . no_hold_retain ) ) ; // SetOption62 - Don't use retain flag on HOLD messages
2019-12-02 09:44:27 +00:00
# ifdef USE_DOMOTICZ
}
# endif // USE_DOMOTICZ
2021-06-11 17:14:12 +01:00
result = ! Settings - > flag3 . button_switch_force_local ; // SetOption61 - Force local operation when button/switch topic is set
2019-12-02 09:44:27 +00:00
} else {
2021-01-18 20:48:04 +00:00
Response_P ( PSTR ( " { \" %s%d \" :{ \" State \" :%d}} " ) , ( key ) ? PSTR ( " Switch " ) : PSTR ( " Button " ) , device , state ) ;
2021-04-04 11:04:36 +01:00
result = XdrvRulesProcess ( 0 ) ;
2019-12-02 09:44:27 +00:00
}
2020-10-20 16:48:49 +01:00
# ifdef USE_PWM_DIMMER
2021-11-09 01:51:41 +00:00
if ( PWM_DIMMER ! = TasmotaGlobal . module_type | | ( ! result & & ! Settings - > flag3 . mqtt_buttons ) ) {
2020-10-20 16:48:49 +01:00
# endif // USE_PWM_DIMMER
2019-12-02 09:44:27 +00:00
int32_t payload_save = XdrvMailbox . payload ;
2020-03-04 17:02:27 +00:00
XdrvMailbox . payload = device_save < < 24 | key < < 16 | state < < 8 | device ;
2019-12-02 09:44:27 +00:00
XdrvCall ( FUNC_ANY_KEY ) ;
XdrvMailbox . payload = payload_save ;
2020-10-20 02:12:41 +01:00
# ifdef USE_PWM_DIMMER
2020-11-02 23:44:07 +00:00
if ( PWM_DIMMER = = TasmotaGlobal . module_type ) result = true ;
2020-10-20 16:48:49 +01:00
}
2020-10-20 02:12:41 +01:00
# endif // USE_PWM_DIMMER
2019-12-02 09:44:27 +00:00
return result ;
}
void ExecuteCommandPower ( uint32_t device , uint32_t state , uint32_t source )
{
// device = Relay number 1 and up
// state 0 = POWER_OFF = Relay Off
2021-06-11 17:14:12 +01:00
// state 1 = POWER_ON = Relay On (turn off after Settings->pulse_timer * 100 mSec if enabled)
2019-12-02 09:44:27 +00:00
// state 2 = POWER_TOGGLE = Toggle relay
// state 3 = POWER_BLINK = Blink relay
// state 4 = POWER_BLINK_STOP = Stop blinking relay
// state 8 = POWER_OFF_NO_STATE = Relay Off and no publishPowerState
// state 9 = POWER_ON_NO_STATE = Relay On and no publishPowerState
// state 10 = POWER_TOGGLE_NO_STATE = Toggle relay and no publishPowerState
// state 16 = POWER_SHOW_STATE = Show power state
// ShowSource(source);
2022-02-24 15:28:45 +00:00
// if (1049 == LANGUAGE_LCID) { return; }
2019-12-02 09:44:27 +00:00
# ifdef USE_SONOFF_IFAN
if ( IsModuleIfan ( ) ) {
2020-10-29 11:58:22 +00:00
TasmotaGlobal . blink_mask & = 1 ; // No blinking on the fan relays
2021-06-11 17:14:12 +01:00
Settings - > flag . interlock = 0 ; // No interlock mode as it is already done by the microcontroller - CMND_INTERLOCK - Enable/disable interlock
Settings - > pulse_timer [ 1 ] = 0 ; // No pulsetimers on the fan relays
Settings - > pulse_timer [ 2 ] = 0 ;
Settings - > pulse_timer [ 3 ] = 0 ;
2019-12-02 09:44:27 +00:00
}
# endif // USE_SONOFF_IFAN
bool publish_power = true ;
if ( ( state > = POWER_OFF_NO_STATE ) & & ( state < = POWER_TOGGLE_NO_STATE ) ) {
state & = 3 ; // POWER_OFF, POWER_ON or POWER_TOGGLE
publish_power = false ;
}
2020-10-30 11:29:48 +00:00
if ( ( device < 1 ) | | ( device > TasmotaGlobal . devices_present ) ) {
2019-12-02 09:44:27 +00:00
device = 1 ;
}
2020-10-29 12:58:50 +00:00
TasmotaGlobal . active_device = device ;
2019-12-02 09:44:27 +00:00
2021-04-22 14:03:56 +01:00
if ( state ! = POWER_SHOW_STATE ) {
SetPulseTimer ( ( device - 1 ) % MAX_PULSETIMERS , 0 ) ;
}
2020-10-02 16:32:45 +01:00
2020-10-28 11:40:52 +00:00
static bool interlock_mutex = false ; // Interlock power command pending
2019-12-02 09:44:27 +00:00
power_t mask = 1 < < ( device - 1 ) ; // Device to control
if ( state < = POWER_TOGGLE ) {
2020-10-28 18:03:39 +00:00
if ( ( TasmotaGlobal . blink_mask & mask ) ) {
TasmotaGlobal . blink_mask & = ( POWER_MASK ^ mask ) ; // Clear device mask
2019-12-02 09:44:27 +00:00
MqttPublishPowerBlinkState ( device ) ;
}
2021-06-11 17:14:12 +01:00
if ( Settings - > flag . interlock & & // CMND_INTERLOCK - Enable/disable interlock
2019-12-02 09:44:27 +00:00
! interlock_mutex & &
2020-10-28 18:03:39 +00:00
( ( POWER_ON = = state ) | | ( ( POWER_TOGGLE = = state ) & & ! ( TasmotaGlobal . power & mask ) ) )
2019-12-02 09:44:27 +00:00
) {
interlock_mutex = true ; // Clear all but masked relay in interlock group if new set requested
2021-03-21 11:00:00 +00:00
bool perform_interlock_delay = false ;
2019-12-02 09:44:27 +00:00
for ( uint32_t i = 0 ; i < MAX_INTERLOCKS ; i + + ) {
2021-06-11 17:14:12 +01:00
if ( Settings - > interlock [ i ] & mask ) { // Find interlock group
2020-10-30 11:29:48 +00:00
for ( uint32_t j = 0 ; j < TasmotaGlobal . devices_present ; j + + ) {
2019-12-02 09:44:27 +00:00
power_t imask = 1 < < j ;
2021-06-11 17:14:12 +01:00
if ( ( Settings - > interlock [ i ] & imask ) & & ( TasmotaGlobal . power & imask ) & & ( mask ! = imask ) ) {
2019-12-02 09:44:27 +00:00
ExecuteCommandPower ( j + 1 , POWER_OFF , SRC_IGNORE ) ;
2021-03-21 11:00:00 +00:00
perform_interlock_delay = true ;
2019-12-02 09:44:27 +00:00
}
}
break ; // An interlocked relay is only present in one group so quit
}
}
2021-03-21 11:00:00 +00:00
if ( perform_interlock_delay ) {
delay ( 50 ) ; // Add some delay to make sure never have more than one relay on
}
2019-12-02 09:44:27 +00:00
interlock_mutex = false ;
}
2021-02-09 03:28:59 +00:00
# ifdef USE_DEVICE_GROUPS
power_t old_power = TasmotaGlobal . power ;
# endif // USE_DEVICE_GROUPS
2019-12-02 09:44:27 +00:00
switch ( state ) {
case POWER_OFF : {
2020-10-28 18:03:39 +00:00
TasmotaGlobal . power & = ( POWER_MASK ^ mask ) ;
2019-12-02 09:44:27 +00:00
break ; }
case POWER_ON :
2020-10-28 18:03:39 +00:00
TasmotaGlobal . power | = mask ;
2019-12-02 09:44:27 +00:00
break ;
case POWER_TOGGLE :
2020-10-28 18:03:39 +00:00
TasmotaGlobal . power ^ = mask ;
2019-12-02 09:44:27 +00:00
}
2020-02-21 15:09:21 +00:00
# ifdef USE_DEVICE_GROUPS
2021-02-09 03:28:59 +00:00
if ( TasmotaGlobal . power ! = old_power & & SRC_REMOTE ! = source & & SRC_RETRY ! = source ) {
2021-02-09 21:10:32 +00:00
power_t dgr_power = TasmotaGlobal . power ;
2021-06-11 17:14:12 +01:00
if ( Settings - > flag4 . multiple_device_groups ) { // SetOption88 - Enable relays in separate device groups
2021-02-09 21:10:32 +00:00
dgr_power = ( dgr_power > > ( device - 1 ) ) & 1 ;
}
2021-02-09 21:42:14 +00:00
SendDeviceGroupMessage ( device , DGR_MSGTYP_UPDATE , DGR_ITEM_POWER , dgr_power ) ;
2020-05-27 04:07:25 +01:00
}
2020-02-21 15:09:21 +00:00
# endif // USE_DEVICE_GROUPS
2020-10-28 18:03:39 +00:00
SetDevicePower ( TasmotaGlobal . power , source ) ;
2019-12-02 09:44:27 +00:00
# ifdef USE_DOMOTICZ
DomoticzUpdatePowerState ( device ) ;
# endif // USE_DOMOTICZ
# ifdef USE_KNX
2020-10-28 18:03:39 +00:00
KnxUpdatePowerState ( device , TasmotaGlobal . power ) ;
2019-12-02 09:44:27 +00:00
# endif // USE_KNX
2021-06-11 17:14:12 +01:00
if ( publish_power & & Settings - > flag3 . hass_tele_on_power ) { // SetOption59 - Send tele/%topic%/STATE in addition to stat/%topic%/RESULT
2019-12-02 09:44:27 +00:00
MqttPublishTeleState ( ) ;
}
2020-10-02 16:32:45 +01:00
// Restart PulseTime if powered On
2021-06-11 17:14:12 +01:00
SetPulseTimer ( ( device - 1 ) % MAX_PULSETIMERS , ( ( ( POWER_ALL_OFF_PULSETIME_ON = = Settings - > poweronstate ) ? ~ TasmotaGlobal . power : TasmotaGlobal . power ) & mask ) ? Settings - > pulse_timer [ ( device - 1 ) % MAX_PULSETIMERS ] : 0 ) ;
2019-12-02 09:44:27 +00:00
}
else if ( POWER_BLINK = = state ) {
2020-10-28 18:03:39 +00:00
if ( ! ( TasmotaGlobal . blink_mask & mask ) ) {
TasmotaGlobal . blink_powersave = ( TasmotaGlobal . blink_powersave & ( POWER_MASK ^ mask ) ) | ( TasmotaGlobal . power & mask ) ; // Save state
TasmotaGlobal . blink_power = ( TasmotaGlobal . power > > ( device - 1 ) ) & 1 ; // Prep to Toggle
2019-12-02 09:44:27 +00:00
}
2020-10-28 16:32:07 +00:00
TasmotaGlobal . blink_timer = millis ( ) + 100 ;
2021-06-11 17:14:12 +01:00
TasmotaGlobal . blink_counter = ( ( ! Settings - > blinkcount ) ? 64000 : ( Settings - > blinkcount * 2 ) ) + 1 ;
2020-10-28 18:03:39 +00:00
TasmotaGlobal . blink_mask | = mask ; // Set device mask
2019-12-02 09:44:27 +00:00
MqttPublishPowerBlinkState ( device ) ;
return ;
}
else if ( POWER_BLINK_STOP = = state ) {
2020-10-28 18:03:39 +00:00
bool flag = ( TasmotaGlobal . blink_mask & mask ) ;
TasmotaGlobal . blink_mask & = ( POWER_MASK ^ mask ) ; // Clear device mask
2019-12-02 09:44:27 +00:00
MqttPublishPowerBlinkState ( device ) ;
if ( flag ) {
2020-10-28 18:03:39 +00:00
ExecuteCommandPower ( device , ( TasmotaGlobal . blink_powersave > > ( device - 1 ) ) & 1 , SRC_IGNORE ) ; // Restore state
2019-12-02 09:44:27 +00:00
}
return ;
}
if ( publish_power ) {
MqttPublishPowerState ( device ) ;
}
}
void StopAllPowerBlink ( void )
{
power_t mask ;
2020-10-30 11:29:48 +00:00
for ( uint32_t i = 1 ; i < = TasmotaGlobal . devices_present ; i + + ) {
2019-12-02 09:44:27 +00:00
mask = 1 < < ( i - 1 ) ;
2020-10-28 18:03:39 +00:00
if ( TasmotaGlobal . blink_mask & mask ) {
TasmotaGlobal . blink_mask & = ( POWER_MASK ^ mask ) ; // Clear device mask
2019-12-02 09:44:27 +00:00
MqttPublishPowerBlinkState ( i ) ;
2020-10-28 18:03:39 +00:00
ExecuteCommandPower ( i , ( TasmotaGlobal . blink_powersave > > ( i - 1 ) ) & 1 , SRC_IGNORE ) ; // Restore state
2019-12-02 09:44:27 +00:00
}
}
}
void MqttShowState ( void )
{
2019-12-22 14:23:52 +00:00
char stemp1 [ TOPSZ ] ;
2019-12-02 09:44:27 +00:00
ResponseAppendTime ( ) ;
ResponseAppend_P ( PSTR ( " , \" " D_JSON_UPTIME " \" : \" %s \" , \" UptimeSec \" :%u " ) , GetUptime ( ) . c_str ( ) , UpTime ( ) ) ;
2020-08-03 17:21:34 +01:00
# ifdef ESP8266
2019-12-02 09:44:27 +00:00
# ifdef USE_ADC_VCC
dtostrfd ( ( double ) ESP . getVcc ( ) / 1000 , 3 , stemp1 ) ;
ResponseAppend_P ( PSTR ( " , \" " D_JSON_VCC " \" :%s " ) , stemp1 ) ;
2020-08-03 17:21:34 +01:00
# endif // USE_ADC_VCC
# endif // ESP8266
2019-12-02 09:44:27 +00:00
2021-02-08 10:34:29 +00:00
ResponseAppend_P ( PSTR ( " , \" " D_JSON_HEAPSIZE " \" :%d, \" SleepMode \" : \" %s \" , \" Sleep \" :%u, \" LoadAvg \" :%u, \" MqttCount \" :%u " ) ,
2021-06-11 17:14:12 +01:00
ESP_getFreeHeap1024 ( ) , GetTextIndexed ( stemp1 , sizeof ( stemp1 ) , Settings - > flag3 . sleep_normal , kSleepMode ) , // SetOption60 - Enable normal sleep instead of dynamic sleep
2020-10-29 15:16:34 +00:00
TasmotaGlobal . sleep , TasmotaGlobal . loop_load_avg , MqttConnectCount ( ) ) ;
2019-12-02 09:44:27 +00:00
2021-11-22 18:29:53 +00:00
# ifdef USE_BERRY
extern void BrShowState ( void ) ;
BrShowState ( ) ;
# endif // USE_BERRY
2020-10-30 11:29:48 +00:00
for ( uint32_t i = 1 ; i < = TasmotaGlobal . devices_present ; i + + ) {
2019-12-02 09:44:27 +00:00
# ifdef USE_LIGHT
if ( ( LightDevice ( ) ) & & ( i > = LightDevice ( ) ) ) {
2020-07-13 14:10:23 +01:00
if ( i = = LightDevice ( ) ) { ResponseLightState ( 1 ) ; } // call it only once
2019-12-02 09:44:27 +00:00
} else {
# endif
2021-08-14 16:40:03 +01:00
ResponseAppend_P ( PSTR ( " , \" %s \" : \" %s \" " ) , GetPowerDevice ( stemp1 , i , sizeof ( stemp1 ) , Settings - > flag . device_index_enable ) , // SetOption26 - Switch between POWER or POWER1
2020-10-28 18:03:39 +00:00
GetStateText ( bitRead ( TasmotaGlobal . power , i - 1 ) ) ) ;
2019-12-02 09:44:27 +00:00
# ifdef USE_SONOFF_IFAN
if ( IsModuleIfan ( ) ) {
ResponseAppend_P ( PSTR ( " , \" " D_CMND_FANSPEED " \" :%d " ) , GetFanspeed ( ) ) ;
break ;
}
# endif // USE_SONOFF_IFAN
# ifdef USE_LIGHT
}
# endif
}
2020-10-30 11:29:48 +00:00
if ( TasmotaGlobal . pwm_present ) {
2019-12-02 09:44:27 +00:00
ResponseAppend_P ( PSTR ( " , " ) ) ;
MqttShowPWMState ( ) ;
}
2020-10-30 11:29:48 +00:00
if ( ! TasmotaGlobal . global_state . wifi_down ) {
2020-06-15 17:27:04 +01:00
int32_t rssi = WiFi . RSSI ( ) ;
2021-06-04 13:51:05 +01:00
ResponseAppend_P ( PSTR ( " , \" " D_JSON_WIFI " \" :{ \" " D_JSON_AP " \" :%d, \" " D_JSON_SSID " \" : \" %s \" , \" " D_JSON_BSSID " \" : \" %s \" , \" " D_JSON_CHANNEL " \" :%d, \" " D_JSON_WIFI_MODE " \" : \" 11%c \" , \" " D_JSON_RSSI " \" :%d, \" " D_JSON_SIGNAL " \" :%d, \" " D_JSON_LINK_COUNT " \" :%d, \" " D_JSON_DOWNTIME " \" : \" %s \" } " ) ,
2021-06-11 17:14:12 +01:00
Settings - > sta_active + 1 , EscapeJSONString ( SettingsText ( SET_STASSID1 + Settings - > sta_active ) ) . c_str ( ) , WiFi . BSSIDstr ( ) . c_str ( ) , WiFi . channel ( ) ,
2021-06-04 13:51:05 +01:00
pgm_read_byte ( & kWifiPhyMode [ WiFi . getPhyMode ( ) & 0x3 ] ) , WifiGetRssiAsQuality ( rssi ) , rssi ,
WifiLinkCount ( ) , WifiDowntime ( ) . c_str ( ) ) ;
2020-06-15 17:27:04 +01:00
}
ResponseJsonEnd ( ) ;
2019-12-02 09:44:27 +00:00
}
void MqttPublishTeleState ( void )
{
2020-10-30 11:29:48 +00:00
ResponseClear ( ) ;
2019-12-02 09:44:27 +00:00
MqttShowState ( ) ;
2021-06-11 17:14:12 +01:00
MqttPublishPrefixTopic_P ( TELE , PSTR ( D_RSLT_STATE ) , Settings - > flag5 . mqtt_state_retain ) ;
2021-04-06 14:23:07 +01:00
XdrvRulesProcess ( 1 ) ;
2019-12-02 09:44:27 +00:00
}
2020-03-16 15:52:22 +00:00
void TempHumDewShow ( bool json , bool pass_on , const char * types , float f_temperature , float f_humidity )
{
if ( json ) {
2020-03-17 15:29:59 +00:00
ResponseAppend_P ( PSTR ( " , \" %s \" :{ " ) , types ) ;
ResponseAppendTHD ( f_temperature , f_humidity ) ;
ResponseJsonEnd ( ) ;
2020-03-16 15:52:22 +00:00
# ifdef USE_DOMOTICZ
if ( pass_on ) {
2020-03-17 15:29:59 +00:00
DomoticzTempHumPressureSensor ( f_temperature , f_humidity ) ;
2020-03-16 15:52:22 +00:00
}
# endif // USE_DOMOTICZ
# ifdef USE_KNX
if ( pass_on ) {
KnxSensor ( KNX_TEMPERATURE , f_temperature ) ;
KnxSensor ( KNX_HUMIDITY , f_humidity ) ;
}
# endif // USE_KNX
# ifdef USE_WEBSERVER
} else {
2020-03-17 15:29:59 +00:00
WSContentSend_THD ( types , f_temperature , f_humidity ) ;
2020-03-16 15:52:22 +00:00
# endif // USE_WEBSERVER
}
}
2020-11-01 12:26:35 +00:00
String GetSwitchText ( uint32_t i ) {
2021-02-05 10:58:24 +00:00
String switch_text = " " ;
if ( i < MAX_SWITCHES_TXT ) {
switch_text = SettingsText ( SET_SWITCH_TXT1 + i ) ;
}
2020-11-01 12:26:35 +00:00
if ( ' \0 ' = = switch_text [ 0 ] ) {
2021-01-18 21:37:36 +00:00
switch_text = F ( D_JSON_SWITCH ) ;
switch_text + = String ( i + 1 ) ;
2020-11-01 12:26:35 +00:00
}
return switch_text ;
}
2022-06-22 22:45:25 +01:00
const char kGlobalValues [ ] PROGMEM = D_JSON_TEMPERATURE " | " D_JSON_HUMIDITY " | " D_JSON_PRESSURE ;
void GetGlobalValues ( void ) {
for ( uint32_t type = 0 ; type < 3 ; type + + ) {
2022-06-22 22:47:58 +01:00
if ( ! Settings - > global_sensor_index [ type ] | | TasmotaGlobal . user_globals [ type ] ) { continue ; }
2022-06-22 22:45:25 +01:00
char key [ 20 ] ;
GetTextIndexed ( key , sizeof ( key ) , type , kGlobalValues ) ;
float value = - 9999 ;
uint32_t idx = 0 ;
char * data = ResponseData ( ) ;
while ( data ) {
data = strstr ( data , key ) ;
if ( data ) {
idx + + ;
data + = strlen ( key ) + 2 ;
float new_value = CharToFloat ( data ) ;
if ( 1 = = idx ) { value = new_value ; }
// AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: %s value%d = %2_f"), key, idx, &new_value);
if ( idx = = Settings - > global_sensor_index [ type ] ) {
value = new_value ;
break ;
}
}
}
if ( value ! = - 9999 ) {
switch ( type ) {
case 0 : // Temperature
TasmotaGlobal . temperature_celsius = ConvertTempToCelsius ( value ) ;
break ;
case 1 : // Humidity
TasmotaGlobal . humidity = value ;
break ;
case 2 : // Pressure
TasmotaGlobal . pressure_hpa = ConvertHgToHpa ( value ) ;
break ;
}
TasmotaGlobal . global_update = TasmotaGlobal . uptime ;
}
}
}
2022-04-16 16:13:26 +01:00
void MqttAppendSensorUnits ( void )
{
if ( ResponseContains_P ( PSTR ( D_JSON_PRESSURE ) ) ) {
ResponseAppend_P ( PSTR ( " , \" " D_JSON_PRESSURE_UNIT " \" : \" %s \" " ) , PressureUnit ( ) . c_str ( ) ) ;
}
if ( ResponseContains_P ( PSTR ( D_JSON_TEMPERATURE ) ) ) {
ResponseAppend_P ( PSTR ( " , \" " D_JSON_TEMPERATURE_UNIT " \" : \" %c \" " ) , TempUnit ( ) ) ;
}
if ( ResponseContains_P ( PSTR ( D_JSON_SPEED ) ) & & Settings - > flag2 . speed_conversion ) {
ResponseAppend_P ( PSTR ( " , \" " D_JSON_SPEED_UNIT " \" : \" %s \" " ) , SpeedUnit ( ) . c_str ( ) ) ;
}
}
2021-11-21 17:54:13 +00:00
bool MqttShowSensor ( bool call_show_sensor )
2019-12-02 09:44:27 +00:00
{
ResponseAppendTime ( ) ;
2021-05-23 13:42:27 +01:00
int json_data_start = ResponseLength ( ) ;
2019-12-02 09:44:27 +00:00
for ( uint32_t i = 0 ; i < MAX_SWITCHES ; i + + ) {
# ifdef USE_TM1638
2021-03-19 09:33:46 +00:00
if ( PinUsed ( GPIO_SWT1 , i ) | | ( PinUsed ( GPIO_TM1638CLK ) & & PinUsed ( GPIO_TM1638DIO ) & & PinUsed ( GPIO_TM1638STB ) ) ) {
2019-12-02 09:44:27 +00:00
# else
2020-04-27 11:54:07 +01:00
if ( PinUsed ( GPIO_SWT1 , i ) ) {
2019-12-02 09:44:27 +00:00
# endif // USE_TM1638
2020-11-01 12:26:35 +00:00
ResponseAppend_P ( PSTR ( " , \" %s \" : \" %s \" " ) , GetSwitchText ( i ) . c_str ( ) , GetStateText ( SwitchState ( i ) ) ) ;
2019-12-02 09:44:27 +00:00
}
}
XsnsCall ( FUNC_JSON_APPEND ) ;
XdrvCall ( FUNC_JSON_APPEND ) ;
2022-06-22 22:45:25 +01:00
GetGlobalValues ( ) ;
if ( TasmotaGlobal . global_update & & Settings - > flag . mqtt_add_global_info ) { // SetOption2 (MQTT) Add global temperature/humidity/pressure info to JSON sensor message
2021-07-13 15:44:28 +01:00
if ( ( TasmotaGlobal . humidity > 0 ) | | ! isnan ( TasmotaGlobal . temperature_celsius ) | | ( TasmotaGlobal . pressure_hpa ! = 0 ) ) {
uint32_t add_comma = 0 ;
ResponseAppend_P ( PSTR ( " , \" Global \" :{ " ) ) ;
if ( ! isnan ( TasmotaGlobal . temperature_celsius ) ) {
float t = ConvertTempToFahrenheit ( TasmotaGlobal . temperature_celsius ) ;
ResponseAppend_P ( PSTR ( " \" " D_JSON_TEMPERATURE " \" :%*_f " ) ,
Settings - > flag2 . temperature_resolution , & t ) ;
add_comma + + ;
}
if ( TasmotaGlobal . humidity > 0 ) {
ResponseAppend_P ( PSTR ( " %s \" " D_JSON_HUMIDITY " \" :%*_f " ) ,
( add_comma ) ? " , " : " " , Settings - > flag2 . humidity_resolution , & TasmotaGlobal . humidity ) ;
add_comma + + ;
}
if ( 2 = = add_comma ) {
float dewpoint = CalcTempHumToDew ( TasmotaGlobal . temperature_celsius , TasmotaGlobal . humidity ) ;
ResponseAppend_P ( PSTR ( " %s \" " D_JSON_DEWPOINT " \" :%*_f " ) ,
( add_comma ) ? " , " : " " , Settings - > flag2 . temperature_resolution , & dewpoint ) ;
}
if ( TasmotaGlobal . pressure_hpa ! = 0 ) {
float p = ConvertPressure ( TasmotaGlobal . pressure_hpa ) ;
float s = ConvertPressureForSeaLevel ( TasmotaGlobal . pressure_hpa ) ;
ResponseAppend_P ( PSTR ( " %s \" " D_JSON_PRESSURE " \" :%*_f, \" " D_JSON_PRESSUREATSEALEVEL " \" :%*_f " ) ,
( add_comma ) ? " , " : " " , Settings - > flag2 . pressure_resolution , & p , Settings - > flag2 . pressure_resolution , & s ) ;
}
ResponseJsonEnd ( ) ;
}
}
2021-05-23 13:42:27 +01:00
bool json_data_available = ( ResponseLength ( ) - json_data_start ) ;
2022-04-16 16:13:26 +01:00
MqttAppendSensorUnits ( ) ;
2019-12-02 09:44:27 +00:00
ResponseJsonEnd ( ) ;
2021-11-21 17:54:13 +00:00
if ( call_show_sensor & & json_data_available ) { XdrvCall ( FUNC_SHOW_SENSOR ) ; }
2019-12-02 09:44:27 +00:00
return json_data_available ;
}
2021-04-05 10:10:53 +01:00
void MqttPublishSensor ( void ) {
2020-10-30 11:29:48 +00:00
ResponseClear ( ) ;
2021-11-21 17:54:13 +00:00
if ( MqttShowSensor ( true ) ) {
2019-12-02 09:44:27 +00:00
MqttPublishTeleSensor ( ) ;
}
}
2021-04-05 10:10:53 +01:00
void MqttPublishTeleperiodSensor ( void ) {
ResponseClear ( ) ;
2021-11-21 17:54:13 +00:00
if ( MqttShowSensor ( true ) ) {
2021-06-11 17:14:12 +01:00
MqttPublishPrefixTopic_P ( TELE , PSTR ( D_RSLT_SENSOR ) , Settings - > flag . mqtt_sensor_retain ) ; // CMND_SENSORRETAIN
2021-04-05 10:10:53 +01:00
XdrvRulesProcess ( 1 ) ;
}
}
2022-03-07 10:37:48 +00:00
void SkipSleep ( bool state ) {
if ( state ) {
TasmotaGlobal . skip_sleep + = 2 ;
} else {
if ( TasmotaGlobal . skip_sleep ) {
TasmotaGlobal . skip_sleep - - ;
}
if ( TasmotaGlobal . skip_sleep ) {
TasmotaGlobal . skip_sleep - - ;
}
}
}
2020-01-18 15:57:48 +00:00
/*********************************************************************************************\
* State loops
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*-------------------------------------------------------------------------------------------*\
* Every second
\ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2019-12-02 09:44:27 +00:00
void PerformEverySecond ( void )
{
2020-10-28 16:32:07 +00:00
TasmotaGlobal . uptime + + ;
2019-12-02 09:44:27 +00:00
2020-10-28 16:32:07 +00:00
if ( POWER_CYCLE_TIME = = TasmotaGlobal . uptime ) {
2019-12-02 09:44:27 +00:00
UpdateQuickPowerCycle ( false ) ;
}
2020-10-28 16:32:07 +00:00
if ( BOOT_LOOP_TIME = = TasmotaGlobal . uptime ) {
2019-12-02 09:44:27 +00:00
RtcRebootReset ( ) ;
2021-06-11 17:14:12 +01:00
Settings - > last_module = Settings - > module ;
2020-11-01 11:12:27 +00:00
2019-12-02 09:44:27 +00:00
# ifdef USE_DEEPSLEEP
2021-06-11 17:14:12 +01:00
if ( ! ( DeepSleepEnabled ( ) & & ! Settings - > flag3 . bootcount_update ) ) { // SetOption76 - (Deepsleep) Enable incrementing bootcount (1) when deepsleep is enabled
2019-12-02 09:44:27 +00:00
# endif
2021-06-11 17:14:12 +01:00
Settings - > bootcount + + ; // Moved to here to stop flash writes during start-up
AddLog ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_APPLICATION D_BOOT_COUNT " %d " ) , Settings - > bootcount ) ;
2019-12-02 09:44:27 +00:00
# ifdef USE_DEEPSLEEP
}
# endif
}
2020-10-29 12:58:50 +00:00
if ( TasmotaGlobal . mqtt_cmnd_blocked_reset ) {
TasmotaGlobal . mqtt_cmnd_blocked_reset - - ;
if ( ! TasmotaGlobal . mqtt_cmnd_blocked_reset ) {
TasmotaGlobal . mqtt_cmnd_blocked = 0 ; // Clean up MQTT cmnd loop block
2020-01-18 15:57:48 +00:00
}
}
2020-10-29 12:37:09 +00:00
if ( TasmotaGlobal . seriallog_timer ) {
TasmotaGlobal . seriallog_timer - - ;
if ( ! TasmotaGlobal . seriallog_timer ) {
2020-10-30 11:29:48 +00:00
if ( TasmotaGlobal . seriallog_level ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_APPLICATION D_SERIAL_LOGGING_DISABLED ) ) ;
2019-12-02 09:44:27 +00:00
}
2020-10-30 11:29:48 +00:00
TasmotaGlobal . seriallog_level = 0 ;
2019-12-02 09:44:27 +00:00
}
}
2020-10-29 12:37:09 +00:00
if ( TasmotaGlobal . syslog_timer ) { // Restore syslog level
TasmotaGlobal . syslog_timer - - ;
if ( ! TasmotaGlobal . syslog_timer ) {
2021-06-11 17:14:12 +01:00
TasmotaGlobal . syslog_level = Settings - > syslog_level ;
if ( Settings - > syslog_level ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_APPLICATION D_SYSLOG_LOGGING_REENABLED ) ) ; // Might trigger disable again (on purpose)
2019-12-02 09:44:27 +00:00
}
}
}
2020-12-22 14:26:07 +00:00
MqttPublishLoggingAsync ( false ) ;
SyslogAsync ( false ) ;
2020-12-18 14:37:20 +00:00
2019-12-02 09:44:27 +00:00
ResetGlobalValues ( ) ;
2021-12-18 13:30:25 +00:00
if ( ( TasmotaGlobal . init_state > = INIT_GPIOS ) & & PinUsed ( GPIO_HEARTBEAT ) ) {
digitalWrite ( Pin ( GPIO_HEARTBEAT ) , ~ TasmotaGlobal . heartbeat_inverted & 1 ) ;
delayMicroseconds ( 50 ) ;
digitalWrite ( Pin ( GPIO_HEARTBEAT ) , TasmotaGlobal . heartbeat_inverted ) ;
}
2021-06-11 17:14:12 +01:00
if ( Settings - > tele_period | | ( 3601 = = TasmotaGlobal . tele_period ) ) {
2020-10-29 12:37:09 +00:00
if ( TasmotaGlobal . tele_period > = 9999 ) {
2020-10-30 11:29:48 +00:00
if ( ! TasmotaGlobal . global_state . network_down ) {
2020-10-29 12:37:09 +00:00
TasmotaGlobal . tele_period = 0 ; // Allow teleperiod once wifi is connected
2019-12-02 09:44:27 +00:00
}
} else {
2020-10-29 12:37:09 +00:00
TasmotaGlobal . tele_period + + ;
2021-06-11 17:14:12 +01:00
if ( TasmotaGlobal . tele_period > = Settings - > tele_period ) {
2020-10-29 12:37:09 +00:00
TasmotaGlobal . tele_period = 0 ;
2019-12-02 09:44:27 +00:00
MqttPublishTeleState ( ) ;
2021-04-05 10:10:53 +01:00
MqttPublishTeleperiodSensor ( ) ;
2019-12-02 09:44:27 +00:00
2020-02-29 15:11:59 +00:00
XsnsCall ( FUNC_AFTER_TELEPERIOD ) ;
2019-12-02 09:44:27 +00:00
XdrvCall ( FUNC_AFTER_TELEPERIOD ) ;
}
}
}
2020-04-07 13:07:00 +01:00
2021-11-22 15:20:26 +00:00
# ifdef ESP8266
2020-04-07 13:07:00 +01:00
// Wifi keep alive to send Gratuitous ARP
wifiKeepAlive ( ) ;
2021-11-22 15:20:26 +00:00
# endif
2020-04-13 16:45:06 +01:00
2020-11-06 14:22:03 +00:00
WifiPollNtp ( ) ;
2020-04-13 16:45:06 +01:00
# ifdef ESP32
2020-10-28 16:32:07 +00:00
if ( 11 = = TasmotaGlobal . uptime ) { // Perform one-time ESP32 houskeeping
ESP_getSketchSize ( ) ; // Init sketchsize as it can take up to 2 seconds
2020-04-13 16:45:06 +01:00
}
# endif
2021-01-13 12:51:49 +00:00
# ifdef USE_UFILESYS
static bool settings_lkg = false ; // Settings saved as Last Known Good
// Copy Settings as Last Known Good if no changes have been saved since 30 minutes
2021-06-11 17:14:12 +01:00
if ( ! settings_lkg & & ( UtcTime ( ) > START_VALID_TIME ) & & ( Settings - > cfg_timestamp < UtcTime ( ) - ( 30 * 60 ) ) ) {
TfsSaveFile ( TASM_FILE_SETTINGS_LKG , ( const uint8_t * ) Settings , sizeof ( TSettings ) ) ;
2021-01-13 12:51:49 +00:00
settings_lkg = true ;
}
# endif
2019-12-02 09:44:27 +00:00
}
/*-------------------------------------------------------------------------------------------*\
* Every 0.1 second
\ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void Every100mSeconds ( void )
{
// As the max amount of sleep = 250 mSec this loop will shift in time...
power_t power_now ;
2020-10-29 12:58:50 +00:00
if ( TasmotaGlobal . latching_relay_pulse ) {
TasmotaGlobal . latching_relay_pulse - - ;
if ( ! TasmotaGlobal . latching_relay_pulse ) SetLatchingRelay ( 0 , 0 ) ;
2019-12-02 09:44:27 +00:00
}
2022-03-07 10:37:48 +00:00
if ( TasmotaGlobal . skip_sleep ) {
TasmotaGlobal . skip_sleep - - ; // Clean up possible residue
}
2019-12-02 09:44:27 +00:00
for ( uint32_t i = 0 ; i < MAX_PULSETIMERS ; i + + ) {
2020-10-28 16:32:07 +00:00
if ( TasmotaGlobal . pulse_timer [ i ] ! = 0L ) { // Timer active?
if ( TimeReached ( TasmotaGlobal . pulse_timer [ i ] ) ) { // Timer finished?
TasmotaGlobal . pulse_timer [ i ] = 0L ; // Turn off this timer
2021-12-04 14:03:15 +00:00
for ( uint32_t j = 0 ; ( i + j ) < TasmotaGlobal . devices_present ; j = j + MAX_PULSETIMERS ) {
2021-06-11 17:14:12 +01:00
ExecuteCommandPower ( i + j + 1 , ( POWER_ALL_OFF_PULSETIME_ON = = Settings - > poweronstate ) ? POWER_ON : POWER_OFF , SRC_PULSETIMER ) ;
2020-10-02 16:32:45 +01:00
}
2019-12-02 09:44:27 +00:00
}
}
}
2020-10-28 18:03:39 +00:00
if ( TasmotaGlobal . blink_mask ) {
2020-10-28 16:32:07 +00:00
if ( TimeReached ( TasmotaGlobal . blink_timer ) ) {
2021-06-11 17:14:12 +01:00
SetNextTimeInterval ( TasmotaGlobal . blink_timer , 100 * Settings - > blinktime ) ;
2020-10-29 12:37:09 +00:00
TasmotaGlobal . blink_counter - - ;
if ( ! TasmotaGlobal . blink_counter ) {
2019-12-02 09:44:27 +00:00
StopAllPowerBlink ( ) ;
} else {
2020-10-28 18:03:39 +00:00
TasmotaGlobal . blink_power ^ = 1 ;
power_now = ( TasmotaGlobal . power & ( POWER_MASK ^ TasmotaGlobal . blink_mask ) ) | ( ( TasmotaGlobal . blink_power ) ? TasmotaGlobal . blink_mask : 0 ) ;
2019-12-02 09:44:27 +00:00
SetDevicePower ( power_now , SRC_IGNORE ) ;
}
}
}
}
/*-------------------------------------------------------------------------------------------*\
* Every 0.25 second
\ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2021-01-18 10:51:24 +00:00
# ifdef USE_BLE_ESP32
// declare the fn
int ExtStopBLE ( ) ;
# endif // USE_BLE_ESP32
2021-02-16 12:00:10 +00:00
bool CommandsReady ( void ) {
bool ready = BACKLOG_EMPTY ;
# ifdef USE_UFILESYS
2021-02-16 15:21:46 +00:00
ready | = UfsExecuteCommandFileReady ( ) ;
2021-02-16 12:00:10 +00:00
# endif // USE_UFILESYS
return ready ;
}
2019-12-02 09:44:27 +00:00
void Every250mSeconds ( void )
{
// As the max amount of sleep = 250 mSec this loop should always be taken...
2020-10-28 11:40:52 +00:00
static uint8_t blinkspeed = 1 ; // LED blink rate
2019-12-02 09:44:27 +00:00
uint32_t blinkinterval = 1 ;
2020-10-29 12:58:50 +00:00
TasmotaGlobal . state_250mS + + ;
TasmotaGlobal . state_250mS & = 0x3 ;
2019-12-02 09:44:27 +00:00
2020-10-30 11:29:48 +00:00
TasmotaGlobal . global_state . network_down = ( TasmotaGlobal . global_state . wifi_down & & TasmotaGlobal . global_state . eth_down ) ? 1 : 0 ;
2020-06-15 17:27:04 +01:00
2021-06-11 17:14:12 +01:00
if ( ! Settings - > flag . global_state ) { // SetOption31 - Control link led blinking
2020-10-30 11:29:48 +00:00
if ( TasmotaGlobal . global_state . data & 0x03 ) { // Network or MQTT problem
if ( TasmotaGlobal . global_state . mqtt_down ) { blinkinterval = 7 ; } // MQTT problem so blink every 2 seconds (slowest)
if ( TasmotaGlobal . global_state . network_down ) { blinkinterval = 3 ; } // Network problem so blink every second (slow)
2020-10-29 11:58:22 +00:00
TasmotaGlobal . blinks = 201 ; // Allow only a single blink in case the problem is solved
2019-12-02 09:44:27 +00:00
}
}
2020-10-29 11:39:44 +00:00
if ( TasmotaGlobal . blinks | | TasmotaGlobal . restart_flag | | TasmotaGlobal . ota_state_flag ) {
2020-10-29 11:58:22 +00:00
if ( TasmotaGlobal . restart_flag | | TasmotaGlobal . ota_state_flag ) { // Overrule blinks and keep led lit
2020-10-30 11:29:48 +00:00
TasmotaGlobal . blinkstate = true ; // Stay lit
2019-12-02 09:44:27 +00:00
} else {
blinkspeed - - ;
if ( ! blinkspeed ) {
blinkspeed = blinkinterval ; // Set interval to 0.2 (default), 1 or 2 seconds
2020-10-30 11:29:48 +00:00
TasmotaGlobal . blinkstate ^ = 1 ; // Blink
2019-12-02 09:44:27 +00:00
}
}
2021-06-11 17:14:12 +01:00
if ( ( ! ( Settings - > ledstate & 0x08 ) ) & & ( ( Settings - > ledstate & 0x06 ) | | ( TasmotaGlobal . blinks > 200 ) | | ( TasmotaGlobal . blinkstate ) ) ) {
2020-10-30 11:29:48 +00:00
SetLedLink ( TasmotaGlobal . blinkstate ) ; // Set led on or off
2019-12-02 09:44:27 +00:00
}
2020-10-30 11:29:48 +00:00
if ( ! TasmotaGlobal . blinkstate ) {
2020-10-29 11:39:44 +00:00
TasmotaGlobal . blinks - - ;
2020-10-29 11:58:22 +00:00
if ( 200 = = TasmotaGlobal . blinks ) { TasmotaGlobal . blinks = 0 ; } // Disable blink
2019-12-02 09:44:27 +00:00
}
}
2021-06-11 17:14:12 +01:00
if ( Settings - > ledstate & 1 & & ( PinUsed ( GPIO_LEDLNK ) | | ! ( TasmotaGlobal . blinks | | TasmotaGlobal . restart_flag | | TasmotaGlobal . ota_state_flag ) ) ) {
bool tstate = TasmotaGlobal . power & Settings - > ledmask ;
2020-04-10 17:24:08 +01:00
# ifdef ESP8266
2020-10-30 11:29:48 +00:00
if ( ( SONOFF_TOUCH = = TasmotaGlobal . module_type ) | | ( SONOFF_T11 = = TasmotaGlobal . module_type ) | | ( SONOFF_T12 = = TasmotaGlobal . module_type ) | | ( SONOFF_T13 = = TasmotaGlobal . module_type ) ) {
2020-10-29 11:58:22 +00:00
tstate = ( ! TasmotaGlobal . power ) ? 1 : 0 ; // As requested invert signal for Touch devices to find them in the dark
2019-12-02 09:44:27 +00:00
}
2020-04-10 17:24:08 +01:00
# endif // ESP8266
2019-12-02 09:44:27 +00:00
SetLedPower ( tstate ) ;
}
2020-12-22 14:26:07 +00:00
// Check if log refresh needed in case of fast buffer fill
MqttPublishLoggingAsync ( true ) ;
SyslogAsync ( true ) ;
2019-12-02 09:44:27 +00:00
/*-------------------------------------------------------------------------------------------*\
* Every second at 0.25 second interval
\ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2020-10-28 11:40:52 +00:00
static int ota_result = 0 ;
static uint8_t ota_retry_counter = OTA_ATTEMPTS ;
2020-10-29 12:58:50 +00:00
switch ( TasmotaGlobal . state_250mS ) {
2019-12-02 09:44:27 +00:00
case 0 : // Every x.0 second
2021-02-16 12:00:10 +00:00
if ( TasmotaGlobal . ota_state_flag & & CommandsReady ( ) ) {
2020-10-29 11:21:24 +00:00
TasmotaGlobal . ota_state_flag - - ;
if ( 2 = = TasmotaGlobal . ota_state_flag ) {
2022-05-10 21:21:34 +01:00
# ifdef CONFIG_IDF_TARGET_ESP32C3
OtaFactoryWrite ( false ) ;
# endif
2021-02-13 06:52:21 +00:00
RtcSettings . ota_loader = 0 ; // Try requested image first
2019-12-02 09:44:27 +00:00
ota_retry_counter = OTA_ATTEMPTS ;
2021-02-13 06:52:21 +00:00
SettingsSave ( 1 ) ; // Free flash for OTA update
2019-12-02 09:44:27 +00:00
}
2020-10-29 11:21:24 +00:00
if ( TasmotaGlobal . ota_state_flag < = 0 ) {
2021-01-18 10:51:24 +00:00
# ifdef USE_BLE_ESP32
ExtStopBLE ( ) ;
# endif // USE_BLE_ESP32
2020-07-20 10:20:58 +01:00
# ifdef USE_COUNTER
2021-02-13 06:52:21 +00:00
CounterInterruptDisable ( true ) ; // Prevent OTA failures on 100Hz counter interrupts
2020-07-20 10:20:58 +01:00
# endif // USE_COUNTER
2019-12-02 09:44:27 +00:00
# ifdef USE_WEBSERVER
2021-06-11 17:14:12 +01:00
if ( Settings - > webserver ) StopWebserver ( ) ;
2019-12-02 09:44:27 +00:00
# endif // USE_WEBSERVER
# ifdef USE_ARILUX_RF
2021-02-13 06:52:21 +00:00
AriluxRfDisable ( ) ; // Prevent restart exception on Arilux Interrupt routine
2019-12-02 09:44:27 +00:00
# endif // USE_ARILUX_RF
2020-10-29 11:21:24 +00:00
TasmotaGlobal . ota_state_flag = 92 ;
2019-12-02 09:44:27 +00:00
ota_result = 0 ;
2021-05-23 15:50:17 +01:00
char full_ota_url [ 200 ] ;
2019-12-02 09:44:27 +00:00
ota_retry_counter - - ;
if ( ota_retry_counter ) {
2020-12-23 17:00:59 +00:00
char ota_url [ TOPSZ ] ;
2021-05-23 15:50:17 +01:00
strlcpy ( full_ota_url , GetOtaUrl ( ota_url , sizeof ( ota_url ) ) , sizeof ( full_ota_url ) ) ;
2021-02-13 06:52:21 +00:00
# ifdef ESP8266
2019-12-02 09:44:27 +00:00
# ifndef FIRMWARE_MINIMAL
if ( RtcSettings . ota_loader ) {
2020-01-17 16:14:53 +00:00
// OTA File too large so try OTA minimal version
// Replace tasmota with tasmota-minimal
// Replace tasmota-DE with tasmota-minimal
// Replace tasmota.bin with tasmota-minimal.bin
// Replace tasmota.xyz with tasmota-minimal.xyz
// Replace tasmota.bin.gz with tasmota-minimal.bin.gz
// Replace tasmota.xyz.gz with tasmota-minimal.xyz.gz
2020-03-04 14:36:37 +00:00
// Replace tasmota.ino.bin with tasmota-minimal.ino.bin
2020-03-04 17:02:27 +00:00
// Replace tasmota.ino.bin.gz with tasmota-minimal.ino.bin.gz
2020-01-17 16:14:53 +00:00
// Replace http://domus1:80/api/arduino/tasmota.bin with http://domus1:80/api/arduino/tasmota-minimal.bin
// Replace http://domus1:80/api/arduino/tasmota.bin.gz with http://domus1:80/api/arduino/tasmota-minimal.bin.gz
// Replace http://domus1:80/api/arduino/tasmota-DE.bin.gz with http://domus1:80/api/arduino/tasmota-minimal.bin.gz
// Replace http://domus1:80/api/ard-uino/tasmota-DE.bin.gz with http://domus1:80/api/ard-uino/tasmota-minimal.bin.gz
2020-03-04 14:36:37 +00:00
// Replace http://192.168.2.17:80/api/arduino/tasmota.bin with http://192.168.2.17:80/api/arduino/tasmota-minimal.bin
// Replace http://192.168.2.17/api/arduino/tasmota.bin.gz with http://192.168.2.17/api/arduino/tasmota-minimal.bin.gz
2020-01-17 16:14:53 +00:00
2021-05-23 15:50:17 +01:00
char * bch = strrchr ( full_ota_url , ' / ' ) ; // Only consider filename after last backslash prevent change of urls having "-" in it
if ( bch = = nullptr ) { bch = full_ota_url ; } // No path found so use filename only
2020-03-04 14:36:37 +00:00
char * ech = strchr ( bch , ' . ' ) ; // Find file type in filename (none, .ino.bin, .ino.bin.gz, .bin, .bin.gz or .gz)
2021-06-06 16:26:01 +01:00
if ( ech = = nullptr ) { ech = full_ota_url + strlen ( full_ota_url ) ; } // Point to '/0' at end of full_ota_url becoming an empty string
2020-03-04 14:36:37 +00:00
2021-01-23 15:26:23 +00:00
//AddLog(LOG_LEVEL_DEBUG, PSTR("OTA: File type [%s]"), ech);
2020-03-04 14:36:37 +00:00
2020-01-17 14:38:03 +00:00
char ota_url_type [ strlen ( ech ) + 1 ] ;
2020-03-04 14:36:37 +00:00
strncpy ( ota_url_type , ech , sizeof ( ota_url_type ) ) ; // Either empty, .ino.bin, .ino.bin.gz, .bin, .bin.gz or .gz
2020-01-17 16:14:53 +00:00
char * pch = strrchr ( bch , ' - ' ) ; // Find last dash (-) and ignore remainder - handles tasmota-DE
if ( pch = = nullptr ) { pch = ech ; } // No dash so ignore filetype
2021-06-06 16:26:01 +01:00
* pch = ' \0 ' ; // full_ota_url = http://domus1:80/api/arduino/tasmota
2021-05-23 15:50:17 +01:00
snprintf_P ( full_ota_url , sizeof ( full_ota_url ) , PSTR ( " %s- " D_JSON_MINIMAL " %s " ) , full_ota_url , ota_url_type ) ; // Minimal filename must be filename-minimal
2019-12-02 09:44:27 +00:00
}
# endif // FIRMWARE_MINIMAL
2021-02-13 06:52:21 +00:00
if ( ota_retry_counter < OTA_ATTEMPTS / 2 ) {
2021-05-23 15:50:17 +01:00
if ( StrCaseStr_P ( full_ota_url , PSTR ( " .gz " ) ) ) {
2021-02-13 06:52:21 +00:00
ota_retry_counter = 1 ;
} else {
2021-05-23 15:50:17 +01:00
strcat_P ( full_ota_url , PSTR ( " .gz " ) ) ;
2021-02-13 06:52:21 +00:00
}
}
# endif // ESP8266
2022-05-01 15:12:54 +01:00
# ifdef ESP32
# ifndef FIRMWARE_MINIMAL
2022-05-11 13:31:39 +01:00
# ifdef USE_WEBCLIENT_HTTPS
if ( TasmotaGlobal . ota_factory ) {
char * bch = strrchr ( full_ota_url , ' / ' ) ; // Only consider filename after last backslash prevent change of urls having "-" in it
if ( bch = = nullptr ) { bch = full_ota_url ; } // No path found so use filename only
char * ech = strchr ( bch , ' . ' ) ; // Find file type in filename (none, .ino.bin, .ino.bin.gz, .bin, .bin.gz or .gz)
if ( ech = = nullptr ) { ech = full_ota_url + strlen ( full_ota_url ) ; } // Point to '/0' at end of full_ota_url becoming an empty string
char ota_url_type [ strlen ( ech ) + 1 ] ;
strncpy ( ota_url_type , ech , sizeof ( ota_url_type ) ) ; // Either empty, .ino.bin, .ino.bin.gz, .bin, .bin.gz or .gz
char * pch = strrchr ( bch , ' - ' ) ; // Find last dash (-) and ignore remainder - handles tasmota-DE
if ( pch = = nullptr ) { pch = ech ; } // No dash so ignore filetype
* pch = ' \0 ' ; // full_ota_url = http://domus1:80/api/arduino/tasmota
2022-05-11 13:45:09 +01:00
snprintf_P ( full_ota_url , sizeof ( full_ota_url ) , PSTR ( " %s-safeboot%s " ) , full_ota_url , ota_url_type ) ; // Safeboot filename must be filename-safeboot
2022-05-11 13:31:39 +01:00
} else
# endif // USE_WEBCLIENT_HTTPS
2022-05-01 15:12:54 +01:00
if ( EspSingleOtaPartition ( ) ) {
2022-05-10 21:21:34 +01:00
# ifdef CONFIG_IDF_TARGET_ESP32C3
OtaFactoryWrite ( true ) ;
# endif
2022-05-05 10:19:39 +01:00
RtcSettings . ota_loader = 1 ; // Try safeboot image next
2022-05-01 15:12:54 +01:00
SettingsSaveAll ( ) ;
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_APPLICATION D_RESTARTING ) ) ;
2022-05-05 10:19:39 +01:00
EspPrepRestartToSafeBoot ( ) ;
2022-05-01 16:32:42 +01:00
EspRestart ( ) ;
2022-05-01 15:12:54 +01:00
}
# endif // FIRMWARE_MINIMAL
# endif // ESP32
2021-03-07 13:41:04 +00:00
char version [ 50 ] ;
2021-04-14 13:33:21 +01:00
snprintf_P ( version , sizeof ( version ) , PSTR ( " %s%s " ) , TasmotaGlobal . version , TasmotaGlobal . image_name ) ;
2021-05-23 15:50:17 +01:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_UPLOAD " %s %s " ) , full_ota_url , version ) ;
2021-11-23 21:15:08 +00:00
# if defined(ESP32) && defined(USE_WEBCLIENT_HTTPS)
HTTPClientLight OTAclient ;
if ( ! OTAclient . begin ( full_ota_url ) ) {
AddLog ( LOG_LEVEL_INFO , " OTA: unsupported protocol " ) ;
ota_result = - 999 ;
} else {
2022-04-06 14:02:15 +01:00
httpUpdateLight . rebootOnUpdate ( false ) ;
2022-05-11 13:31:39 +01:00
httpUpdateLight . setFactory ( TasmotaGlobal . ota_factory ) ;
2021-11-23 21:15:08 +00:00
ota_result = ( HTTP_UPDATE_FAILED ! = httpUpdateLight . update ( OTAclient , version ) ) ;
}
# else // standard OTA over HTTP
2019-12-02 09:44:27 +00:00
WiFiClient OTAclient ;
2022-04-06 14:02:15 +01:00
ESPhttpUpdate . rebootOnUpdate ( false ) ;
2021-05-23 15:50:17 +01:00
ota_result = ( HTTP_UPDATE_FAILED ! = ESPhttpUpdate . update ( OTAclient , full_ota_url , version ) ) ;
2021-11-23 21:15:08 +00:00
# endif
2019-12-02 09:44:27 +00:00
if ( ! ota_result ) {
# ifndef FIRMWARE_MINIMAL
int ota_error = ESPhttpUpdate . getLastError ( ) ;
DEBUG_CORE_LOG ( PSTR ( " OTA: Error %d " ) , ota_error ) ;
2021-02-13 06:52:21 +00:00
# ifdef ESP8266
2019-12-02 09:44:27 +00:00
if ( ( HTTP_UE_TOO_LESS_SPACE = = ota_error ) | | ( HTTP_UE_BIN_FOR_WRONG_FLASH = = ota_error ) ) {
2021-02-13 06:52:21 +00:00
RtcSettings . ota_loader = 1 ; // Try minimal image next
2019-12-02 09:44:27 +00:00
}
2021-02-13 06:52:21 +00:00
# endif // ESP8266
2019-12-02 09:44:27 +00:00
# endif // FIRMWARE_MINIMAL
2021-02-13 06:52:21 +00:00
TasmotaGlobal . ota_state_flag = 2 ; // Upgrade failed - retry
2019-12-02 09:44:27 +00:00
}
}
}
2021-02-13 06:52:21 +00:00
if ( 90 = = TasmotaGlobal . ota_state_flag ) { // Allow MQTT to reconnect
2020-10-29 11:21:24 +00:00
TasmotaGlobal . ota_state_flag = 0 ;
2019-12-20 14:12:44 +00:00
Response_P ( PSTR ( " { \" " D_CMND_UPGRADE " \" : \" " ) ) ;
2019-12-02 09:44:27 +00:00
if ( ota_result ) {
2020-11-07 14:43:52 +00:00
ResponseAppend_P ( PSTR ( D_JSON_SUCCESSFUL " . " D_JSON_RESTARTING ) ) ;
TasmotaGlobal . restart_flag = 2 ;
2019-12-02 09:44:27 +00:00
} else {
2019-12-20 14:12:44 +00:00
ResponseAppend_P ( PSTR ( D_JSON_FAILED " %s " ) , ESPhttpUpdate . getLastErrorString ( ) . c_str ( ) ) ;
2019-12-02 09:44:27 +00:00
}
2019-12-20 14:12:44 +00:00
ResponseAppend_P ( PSTR ( " \" } " ) ) ;
2021-02-13 06:52:21 +00:00
// TasmotaGlobal.restart_flag = 2; // Restart anyway to keep memory clean webserver
2021-04-07 14:07:05 +01:00
MqttPublishPrefixTopicRulesProcess_P ( STAT , PSTR ( D_CMND_UPGRADE ) ) ;
2020-07-20 10:20:58 +01:00
# ifdef USE_COUNTER
CounterInterruptDisable ( false ) ;
# endif // USE_COUNTER
2019-12-02 09:44:27 +00:00
}
}
break ;
case 1 : // Every x.25 second
if ( MidnightNow ( ) ) {
XsnsCall ( FUNC_SAVE_AT_MIDNIGHT ) ;
}
2021-05-05 13:18:43 +01:00
2021-02-16 12:00:10 +00:00
if ( TasmotaGlobal . save_data_counter & & CommandsReady ( ) ) {
2020-10-29 12:58:50 +00:00
TasmotaGlobal . save_data_counter - - ;
if ( TasmotaGlobal . save_data_counter < = 0 ) {
2021-06-11 17:14:12 +01:00
if ( Settings - > flag . save_state ) { // SetOption0 - Save power state and use after restart
2019-12-02 09:44:27 +00:00
power_t mask = POWER_MASK ;
2020-10-30 11:29:48 +00:00
for ( uint32_t i = 0 ; i < TasmotaGlobal . devices_present ; i + + ) {
2021-06-11 17:14:12 +01:00
if ( ( Settings - > pulse_timer [ i % MAX_PULSETIMERS ] > 0 ) & & ( Settings - > pulse_timer [ i % MAX_PULSETIMERS ] < 30 ) ) { // 3 seconds
2019-12-02 09:44:27 +00:00
mask & = ~ ( 1 < < i ) ;
}
}
2021-06-11 17:14:12 +01:00
if ( ! ( ( Settings - > power & mask ) = = ( TasmotaGlobal . power & mask ) ) ) {
Settings - > power = TasmotaGlobal . power ;
2019-12-02 09:44:27 +00:00
}
} else {
2021-06-11 17:14:12 +01:00
Settings - > power = 0 ;
2019-12-02 09:44:27 +00:00
}
2020-10-29 11:21:24 +00:00
if ( ! TasmotaGlobal . restart_flag ) { SettingsSave ( 0 ) ; }
2021-06-11 17:14:12 +01:00
TasmotaGlobal . save_data_counter = Settings - > save_data ;
2019-12-02 09:44:27 +00:00
}
}
2021-05-05 13:18:43 +01:00
2021-02-16 12:00:10 +00:00
if ( TasmotaGlobal . restart_flag & & CommandsReady ( ) ) {
2021-05-05 13:18:43 +01:00
if ( ( 214 = = TasmotaGlobal . restart_flag ) | | // Reset 4
( 215 = = TasmotaGlobal . restart_flag ) | | // Reset 5
( 216 = = TasmotaGlobal . restart_flag ) ) { // Reset 6
2019-12-16 14:13:57 +00:00
// Backup current SSIDs and Passwords
char storage_ssid1 [ strlen ( SettingsText ( SET_STASSID1 ) ) + 1 ] ;
strncpy ( storage_ssid1 , SettingsText ( SET_STASSID1 ) , sizeof ( storage_ssid1 ) ) ;
char storage_ssid2 [ strlen ( SettingsText ( SET_STASSID2 ) ) + 1 ] ;
strncpy ( storage_ssid2 , SettingsText ( SET_STASSID2 ) , sizeof ( storage_ssid2 ) ) ;
char storage_pass1 [ strlen ( SettingsText ( SET_STAPWD1 ) ) + 1 ] ;
strncpy ( storage_pass1 , SettingsText ( SET_STAPWD1 ) , sizeof ( storage_pass1 ) ) ;
char storage_pass2 [ strlen ( SettingsText ( SET_STAPWD2 ) ) + 1 ] ;
strncpy ( storage_pass2 , SettingsText ( SET_STAPWD2 ) , sizeof ( storage_pass2 ) ) ;
char storage_mqtthost [ strlen ( SettingsText ( SET_MQTT_HOST ) ) + 1 ] ;
strncpy ( storage_mqtthost , SettingsText ( SET_MQTT_HOST ) , sizeof ( storage_mqtthost ) ) ;
char storage_mqttuser [ strlen ( SettingsText ( SET_MQTT_USER ) ) + 1 ] ;
strncpy ( storage_mqttuser , SettingsText ( SET_MQTT_USER ) , sizeof ( storage_mqttuser ) ) ;
char storage_mqttpwd [ strlen ( SettingsText ( SET_MQTT_PWD ) ) + 1 ] ;
strncpy ( storage_mqttpwd , SettingsText ( SET_MQTT_PWD ) , sizeof ( storage_mqttpwd ) ) ;
char storage_mqtttopic [ strlen ( SettingsText ( SET_MQTT_TOPIC ) ) + 1 ] ;
strncpy ( storage_mqtttopic , SettingsText ( SET_MQTT_TOPIC ) , sizeof ( storage_mqtttopic ) ) ;
2021-06-11 17:14:12 +01:00
uint16_t mqtt_port = Settings - > mqtt_port ;
2019-12-16 14:13:57 +00:00
2020-10-29 11:21:24 +00:00
// if (216 == TasmotaGlobal.restart_flag) {
2019-12-16 14:13:57 +00:00
// Backup mqtt host, port, client, username and password
// }
2021-05-05 13:18:43 +01:00
if ( ( 215 = = TasmotaGlobal . restart_flag ) | | // Reset 5
( 216 = = TasmotaGlobal . restart_flag ) ) { // Reset 6
2021-01-12 13:54:12 +00:00
SettingsErase ( 2 ) ; // Erase all flash from program end to end of physical excluding optional filesystem
2019-12-02 09:44:27 +00:00
}
SettingsDefault ( ) ;
2019-12-16 14:13:57 +00:00
// Restore current SSIDs and Passwords
SettingsUpdateText ( SET_STASSID1 , storage_ssid1 ) ;
SettingsUpdateText ( SET_STASSID2 , storage_ssid2 ) ;
SettingsUpdateText ( SET_STAPWD1 , storage_pass1 ) ;
SettingsUpdateText ( SET_STAPWD2 , storage_pass2 ) ;
2021-05-05 13:18:43 +01:00
if ( 216 = = TasmotaGlobal . restart_flag ) { // Reset 6
2019-12-16 14:13:57 +00:00
// Restore the mqtt host, port, client, username and password
SettingsUpdateText ( SET_MQTT_HOST , storage_mqtthost ) ;
SettingsUpdateText ( SET_MQTT_USER , storage_mqttuser ) ;
SettingsUpdateText ( SET_MQTT_PWD , storage_mqttpwd ) ;
SettingsUpdateText ( SET_MQTT_TOPIC , storage_mqtttopic ) ;
2021-06-11 17:14:12 +01:00
Settings - > mqtt_port = mqtt_port ;
2019-12-02 09:44:27 +00:00
}
2021-05-05 13:18:43 +01:00
TasmotaGlobal . restart_flag = 3 ; // Finish backlog then Restart 1
2019-12-02 09:44:27 +00:00
}
2021-05-05 13:18:43 +01:00
else if ( 213 = = TasmotaGlobal . restart_flag ) { // Reset 3
2019-12-02 09:44:27 +00:00
SettingsSdkErase ( ) ; // Erase flash SDK parameters
2021-05-05 13:18:43 +01:00
TasmotaGlobal . restart_flag = 2 ; // Restart 1
2019-12-02 09:44:27 +00:00
}
2021-05-05 13:18:43 +01:00
else if ( 212 = = TasmotaGlobal . restart_flag ) { // Reset 2
2019-12-02 09:44:27 +00:00
SettingsErase ( 0 ) ; // Erase all flash from program end to end of physical flash
2021-05-05 13:18:43 +01:00
TasmotaGlobal . restart_flag = 211 ; // Reset 1
2019-12-02 09:44:27 +00:00
}
2021-05-05 13:18:43 +01:00
if ( 211 = = TasmotaGlobal . restart_flag ) { // Reset 1
2019-12-02 09:44:27 +00:00
SettingsDefault ( ) ;
2021-05-05 13:18:43 +01:00
TasmotaGlobal . restart_flag = 3 ; // Finish backlog then Restart 1
2019-12-02 09:44:27 +00:00
}
2021-05-05 13:18:43 +01:00
if ( 2 = = TasmotaGlobal . restart_flag ) { // Restart 1
2019-12-02 09:44:27 +00:00
SettingsSaveAll ( ) ;
}
2021-05-05 13:18:43 +01:00
2020-10-29 11:21:24 +00:00
TasmotaGlobal . restart_flag - - ;
if ( TasmotaGlobal . restart_flag < = 0 ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_APPLICATION " %s " ) , ( TasmotaGlobal . restart_halt ) ? PSTR ( " Halted " ) : PSTR ( D_RESTARTING ) ) ;
2019-12-02 09:44:27 +00:00
EspRestart ( ) ;
}
}
break ;
case 2 : // Every x.5 second
2021-06-11 17:14:12 +01:00
if ( Settings - > flag4 . network_wifi ) {
2020-10-29 11:39:44 +00:00
WifiCheck ( TasmotaGlobal . wifi_state_flag ) ;
TasmotaGlobal . wifi_state_flag = WIFI_RESTART ;
2021-04-22 17:10:26 +01:00
} else {
WifiDisable ( ) ;
2020-06-15 17:27:04 +01:00
}
2019-12-02 09:44:27 +00:00
break ;
case 3 : // Every x.75 second
2020-10-30 11:29:48 +00:00
if ( ! TasmotaGlobal . global_state . network_down ) {
2020-06-15 17:27:04 +01:00
# ifdef FIRMWARE_MINIMAL
2022-05-10 21:21:34 +01:00
# ifdef CONFIG_IDF_TARGET_ESP32C3
if ( OtaFactoryRead ( ) ) {
OtaFactoryWrite ( false ) ;
TasmotaGlobal . ota_state_flag = 3 ;
}
2022-05-11 09:41:32 +01:00
# endif
2020-06-15 17:27:04 +01:00
if ( 1 = = RtcSettings . ota_loader ) {
RtcSettings . ota_loader = 0 ;
2020-10-29 11:21:24 +00:00
TasmotaGlobal . ota_state_flag = 3 ;
2020-06-15 17:27:04 +01:00
}
# endif // FIRMWARE_MINIMAL
# ifdef USE_DISCOVERY
StartMdns ( ) ;
# endif // USE_DISCOVERY
# ifdef USE_WEBSERVER
2021-06-11 17:14:12 +01:00
if ( Settings - > webserver ) {
2020-06-15 17:27:04 +01:00
# ifdef ESP8266
2021-06-11 17:14:12 +01:00
if ( ! WifiIsInManagerMode ( ) ) { StartWebserver ( Settings - > webserver , WiFi . localIP ( ) ) ; }
2020-11-28 11:46:17 +00:00
# endif // ESP8266
# ifdef ESP32
2020-06-15 17:27:04 +01:00
# ifdef USE_ETHERNET
2021-06-11 17:14:12 +01:00
StartWebserver ( Settings - > webserver , ( EthernetLocalIP ( ) ) ? EthernetLocalIP ( ) : WiFi . localIP ( ) ) ;
2020-06-15 17:27:04 +01:00
# else
2021-06-11 17:14:12 +01:00
StartWebserver ( Settings - > webserver , WiFi . localIP ( ) ) ;
2020-06-15 17:27:04 +01:00
# endif
2020-11-28 11:46:17 +00:00
# endif // ESP32
2020-06-15 17:27:04 +01:00
# ifdef USE_DISCOVERY
# ifdef WEBSERVER_ADVERTISE
MdnsAddServiceHttp ( ) ;
# endif // WEBSERVER_ADVERTISE
# endif // USE_DISCOVERY
} else {
StopWebserver ( ) ;
}
# ifdef USE_EMULATION
2021-06-11 17:14:12 +01:00
if ( Settings - > flag2 . emulation ) { UdpConnect ( ) ; }
2020-06-15 17:27:04 +01:00
# endif // USE_EMULATION
# endif // USE_WEBSERVER
# ifdef USE_DEVICE_GROUPS
DeviceGroupsStart ( ) ;
# endif // USE_DEVICE_GROUPS
# ifdef USE_KNX
2021-06-11 17:14:12 +01:00
if ( ! knx_started & & Settings - > flag . knx_enabled ) { // CMND_KNX_ENABLED
2020-06-15 17:27:04 +01:00
KNXStart ( ) ;
knx_started = true ;
}
# endif // USE_KNX
MqttCheck ( ) ;
} else {
# ifdef USE_EMULATION
UdpDisconnect ( ) ;
# endif // USE_EMULATION
# ifdef USE_DEVICE_GROUPS
DeviceGroupsStop ( ) ;
# endif // USE_DEVICE_GROUPS
# ifdef USE_KNX
knx_started = false ;
# endif // USE_KNX
}
2019-12-02 09:44:27 +00:00
break ;
}
}
# ifdef USE_ARDUINO_OTA
/*********************************************************************************************\
* Allow updating via the Arduino OTA - protocol .
*
* - Once started disables current wifi clients and udp
* - Perform restart when done to re - init wifi clients
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool arduino_ota_triggered = false ;
uint16_t arduino_ota_progress_dot_count = 0 ;
void ArduinoOTAInit ( void )
{
ArduinoOTA . setPort ( 8266 ) ;
2020-06-15 17:27:04 +01:00
ArduinoOTA . setHostname ( NetworkHostname ( ) ) ;
2020-01-03 15:50:56 +00:00
if ( strlen ( SettingsText ( SET_WEBPWD ) ) ) {
ArduinoOTA . setPassword ( SettingsText ( SET_WEBPWD ) ) ;
}
2019-12-02 09:44:27 +00:00
ArduinoOTA . onStart ( [ ] ( )
{
SettingsSave ( 1 ) ; // Free flash for OTA update
# ifdef USE_WEBSERVER
2021-06-11 17:14:12 +01:00
if ( Settings - > webserver ) { StopWebserver ( ) ; }
2019-12-02 09:44:27 +00:00
# endif // USE_WEBSERVER
# ifdef USE_ARILUX_RF
AriluxRfDisable ( ) ; // Prevent restart exception on Arilux Interrupt routine
# endif // USE_ARILUX_RF
2021-06-11 17:14:12 +01:00
if ( Settings - > flag . mqtt_enabled ) {
2019-12-02 09:44:27 +00:00
MqttDisconnect ( ) ; // SetOption3 - Enable MQTT
}
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_UPLOAD " Arduino OTA " D_UPLOAD_STARTED ) ) ;
2019-12-02 09:44:27 +00:00
arduino_ota_triggered = true ;
arduino_ota_progress_dot_count = 0 ;
delay ( 100 ) ; // Allow time for message xfer
} ) ;
ArduinoOTA . onProgress ( [ ] ( unsigned int progress , unsigned int total )
{
2020-10-30 11:29:48 +00:00
if ( ( LOG_LEVEL_DEBUG < = TasmotaGlobal . seriallog_level ) ) {
2019-12-02 09:44:27 +00:00
arduino_ota_progress_dot_count + + ;
2022-05-06 13:57:03 +01:00
TasConsole . printf ( " . " ) ;
if ( ! ( arduino_ota_progress_dot_count % 80 ) ) { TasConsole . println ( ) ; }
2019-12-02 09:44:27 +00:00
}
} ) ;
ArduinoOTA . onError ( [ ] ( ota_error_t error )
{
/*
From ArduinoOTA . h :
typedef enum { OTA_AUTH_ERROR , OTA_BEGIN_ERROR , OTA_CONNECT_ERROR , OTA_RECEIVE_ERROR , OTA_END_ERROR } ota_error_t ;
*/
char error_str [ 100 ] ;
2022-05-06 13:57:03 +01:00
if ( ( LOG_LEVEL_DEBUG < = TasmotaGlobal . seriallog_level ) & & arduino_ota_progress_dot_count ) { TasConsole . println ( ) ; }
2019-12-02 09:44:27 +00:00
switch ( error ) {
case OTA_BEGIN_ERROR : strncpy_P ( error_str , PSTR ( D_UPLOAD_ERR_2 ) , sizeof ( error_str ) ) ; break ;
case OTA_RECEIVE_ERROR : strncpy_P ( error_str , PSTR ( D_UPLOAD_ERR_5 ) , sizeof ( error_str ) ) ; break ;
case OTA_END_ERROR : strncpy_P ( error_str , PSTR ( D_UPLOAD_ERR_7 ) , sizeof ( error_str ) ) ; break ;
default :
snprintf_P ( error_str , sizeof ( error_str ) , PSTR ( D_UPLOAD_ERROR_CODE " %d " ) , error ) ;
}
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_UPLOAD " Arduino OTA %s. " D_RESTARTING ) , error_str ) ;
2019-12-02 09:44:27 +00:00
EspRestart ( ) ;
} ) ;
ArduinoOTA . onEnd ( [ ] ( )
{
2022-05-06 13:57:03 +01:00
if ( ( LOG_LEVEL_DEBUG < = TasmotaGlobal . seriallog_level ) ) { TasConsole . println ( ) ; }
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_UPLOAD " Arduino OTA " D_SUCCESSFUL " . " D_RESTARTING ) ) ;
2019-12-02 09:44:27 +00:00
EspRestart ( ) ;
} ) ;
ArduinoOTA . begin ( ) ;
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_UPLOAD " Arduino OTA " D_ENABLED " " D_PORT " 8266 " ) ) ;
2019-12-02 09:44:27 +00:00
}
2020-01-03 15:50:56 +00:00
void ArduinoOtaLoop ( void )
{
MDNS . update ( ) ;
ArduinoOTA . handle ( ) ;
// Once OTA is triggered, only handle that and dont do other stuff. (otherwise it fails)
while ( arduino_ota_triggered ) { ArduinoOTA . handle ( ) ; }
}
2019-12-02 09:44:27 +00:00
# endif // USE_ARDUINO_OTA
/********************************************************************************************/
void SerialInput ( void )
{
2020-10-28 11:40:52 +00:00
static uint32_t serial_polling_window = 0 ;
static bool serial_buffer_overrun = false ;
2019-12-02 09:44:27 +00:00
while ( Serial . available ( ) ) {
// yield();
delay ( 0 ) ;
2020-10-30 11:29:48 +00:00
TasmotaGlobal . serial_in_byte = Serial . read ( ) ;
2019-12-02 09:44:27 +00:00
2020-10-29 11:21:24 +00:00
if ( 0 = = TasmotaGlobal . serial_in_byte_counter ) {
2020-05-11 14:27:29 +01:00
serial_buffer_overrun = false ;
}
2020-10-29 11:21:24 +00:00
else if ( ( TasmotaGlobal . serial_in_byte_counter = = INPUT_BUFFER_SIZE )
2020-05-11 14:27:29 +01:00
# ifdef ESP8266
2020-05-19 14:52:10 +01:00
| | Serial . hasOverrun ( )
2020-05-11 14:27:29 +01:00
# endif
) {
serial_buffer_overrun = true ;
}
2020-04-10 17:24:08 +01:00
# ifdef ESP8266
2019-12-02 09:44:27 +00:00
/*-------------------------------------------------------------------------------------------*\
* Sonoff dual and ch4 19200 baud serial interface
\ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2020-10-30 11:29:48 +00:00
if ( ( SONOFF_DUAL = = TasmotaGlobal . module_type ) | | ( CH4 = = TasmotaGlobal . module_type ) ) {
TasmotaGlobal . serial_in_byte = ButtonSerial ( TasmotaGlobal . serial_in_byte ) ;
2019-12-02 09:44:27 +00:00
}
2020-04-10 17:24:08 +01:00
# endif // ESP8266
2019-12-02 09:44:27 +00:00
/*-------------------------------------------------------------------------------------------*/
if ( XdrvCall ( FUNC_SERIAL ) ) {
2020-10-29 11:21:24 +00:00
TasmotaGlobal . serial_in_byte_counter = 0 ;
2019-12-02 09:44:27 +00:00
Serial . flush ( ) ;
return ;
}
/*-------------------------------------------------------------------------------------------*/
2022-06-06 16:48:40 +01:00
if ( TasmotaGlobal . serial_in_byte > 127 & & ! Settings - > flag . mqtt_serial_raw ) { // Discard binary data above 127 if no raw reception allowed - CMND_SERIALSEND3
2020-10-29 11:21:24 +00:00
TasmotaGlobal . serial_in_byte_counter = 0 ;
2019-12-02 09:44:27 +00:00
Serial . flush ( ) ;
return ;
}
2022-06-06 16:48:40 +01:00
if ( ! Settings - > flag . mqtt_serial ) { // SerialSend active - CMND_SERIALSEND and CMND_SERIALLOG
if ( isprint ( TasmotaGlobal . serial_in_byte ) ) { // Any char between 32 and 127
if ( TasmotaGlobal . serial_in_byte_counter < INPUT_BUFFER_SIZE - 1 ) { // Add char to string if it still fits
2020-10-30 11:29:48 +00:00
TasmotaGlobal . serial_in_buffer [ TasmotaGlobal . serial_in_byte_counter + + ] = TasmotaGlobal . serial_in_byte ;
2019-12-02 09:44:27 +00:00
} else {
2022-06-06 16:48:40 +01:00
serial_buffer_overrun = true ; // Signal overrun but continue reading input to flush until '\n' (EOL)
2019-12-02 09:44:27 +00:00
}
}
} else {
2022-06-06 16:48:40 +01:00
if ( TasmotaGlobal . serial_in_byte | | Settings - > flag . mqtt_serial_raw ) { // Any char between 1 and 127 or any char (0 - 255) - CMND_SERIALSEND3
bool in_byte_is_delimiter = // Char is delimiter when...
2021-06-11 17:14:12 +01:00
( ( ( Settings - > serial_delimiter < 128 ) & & ( TasmotaGlobal . serial_in_byte = = Settings - > serial_delimiter ) ) | | // Any char between 1 and 127 and being delimiter
( ( Settings - > serial_delimiter = = 128 ) & & ! isprint ( TasmotaGlobal . serial_in_byte ) ) ) & & // Any char not between 32 and 127
2022-06-06 16:48:40 +01:00
! Settings - > flag . mqtt_serial_raw ; // In raw mode (CMND_SERIALSEND3) there is never a delimiter
2020-05-23 16:07:52 +01:00
2022-06-06 16:48:40 +01:00
if ( ( TasmotaGlobal . serial_in_byte_counter < INPUT_BUFFER_SIZE - 1 ) & & // Add char to string if it still fits and ...
! in_byte_is_delimiter ) { // Char is not a delimiter
2020-10-30 11:29:48 +00:00
TasmotaGlobal . serial_in_buffer [ TasmotaGlobal . serial_in_byte_counter + + ] = TasmotaGlobal . serial_in_byte ;
2020-05-23 16:30:48 +01:00
}
2020-05-26 11:35:21 +01:00
2022-06-06 16:48:40 +01:00
if ( ( TasmotaGlobal . serial_in_byte_counter > = INPUT_BUFFER_SIZE - 1 ) | | // Send message when buffer is full or ...
in_byte_is_delimiter ) { // Char is delimiter
serial_polling_window = 0 ; // Reception done - send mqtt
2019-12-02 09:44:27 +00:00
break ;
}
2020-05-23 16:30:48 +01:00
2022-06-06 16:48:40 +01:00
serial_polling_window = millis ( ) ; // Wait for next char
2019-12-02 09:44:27 +00:00
}
}
# ifdef USE_SONOFF_SC
/*-------------------------------------------------------------------------------------------*\
* Sonoff SC 19200 baud serial interface
\ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2020-10-30 11:29:48 +00:00
if ( SONOFF_SC = = TasmotaGlobal . module_type ) {
2022-06-06 16:48:40 +01:00
if ( TasmotaGlobal . serial_in_byte = = ' \x1B ' ) { // Sonoff SC status from ATMEGA328P
TasmotaGlobal . serial_in_buffer [ TasmotaGlobal . serial_in_byte_counter ] = 0 ; // Serial data completed
2020-10-30 11:29:48 +00:00
SonoffScSerialInput ( TasmotaGlobal . serial_in_buffer ) ;
2020-10-29 11:21:24 +00:00
TasmotaGlobal . serial_in_byte_counter = 0 ;
2019-12-02 09:44:27 +00:00
Serial . flush ( ) ;
return ;
}
} else
# endif // USE_SONOFF_SC
/*-------------------------------------------------------------------------------------------*/
2022-05-06 13:57:03 +01:00
# ifdef ESP32
if ( tasconsole_serial ) {
# endif // ESP32
2022-06-06 16:48:40 +01:00
if ( ! Settings - > flag . mqtt_serial & & ( TasmotaGlobal . serial_in_byte = = ' \n ' ) ) { // CMND_SERIALSEND and CMND_SERIALLOG
TasmotaGlobal . serial_in_buffer [ TasmotaGlobal . serial_in_byte_counter ] = 0 ; // Serial data completed
2021-06-11 17:14:12 +01:00
TasmotaGlobal . seriallog_level = ( Settings - > seriallog_level < LOG_LEVEL_INFO ) ? ( uint8_t ) LOG_LEVEL_INFO : Settings - > seriallog_level ;
2020-05-11 14:27:29 +01:00
if ( serial_buffer_overrun ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_COMMAND " Serial buffer overrun " ) ) ;
2020-05-11 14:27:29 +01:00
} else {
2021-06-05 10:47:09 +01:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_COMMAND " %s " ) , TasmotaGlobal . serial_in_buffer ) ;
2020-10-30 11:29:48 +00:00
ExecuteCommand ( TasmotaGlobal . serial_in_buffer , SRC_SERIAL ) ;
2020-05-11 14:27:29 +01:00
}
2020-10-29 11:21:24 +00:00
TasmotaGlobal . serial_in_byte_counter = 0 ;
2019-12-02 09:44:27 +00:00
serial_polling_window = 0 ;
Serial . flush ( ) ;
return ;
}
2022-05-06 13:57:03 +01:00
# ifdef ESP32
}
# endif // ESP32
} // endWhile
2019-12-02 09:44:27 +00:00
2021-06-11 17:14:12 +01:00
if ( Settings - > flag . mqtt_serial & & TasmotaGlobal . serial_in_byte_counter & & ( millis ( ) > ( serial_polling_window + SERIAL_POLLING ) ) ) { // CMND_SERIALSEND and CMND_SERIALLOG
2022-06-06 16:48:40 +01:00
TasmotaGlobal . serial_in_buffer [ TasmotaGlobal . serial_in_byte_counter ] = 0 ; // Serial data completed
2021-06-11 17:14:12 +01:00
bool assume_json = ( ! Settings - > flag . mqtt_serial_raw & & ( TasmotaGlobal . serial_in_buffer [ 0 ] = = ' { ' ) ) ;
2020-05-26 11:35:21 +01:00
2021-03-31 17:00:49 +01:00
if ( serial_buffer_overrun ) {
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_COMMAND " Serial buffer overrun " ) ) ;
}
2020-05-24 16:15:06 +01:00
Response_P ( PSTR ( " { \" " D_JSON_SERIALRECEIVED " \" : " ) ) ;
if ( assume_json ) {
2020-10-30 11:29:48 +00:00
ResponseAppend_P ( TasmotaGlobal . serial_in_buffer ) ;
2020-05-24 16:15:06 +01:00
} else {
ResponseAppend_P ( PSTR ( " \" " ) ) ;
2021-06-11 17:14:12 +01:00
if ( Settings - > flag . mqtt_serial_raw ) {
2020-10-29 11:21:24 +00:00
char hex_char [ ( TasmotaGlobal . serial_in_byte_counter * 2 ) + 2 ] ;
2020-10-30 11:29:48 +00:00
ResponseAppend_P ( ToHex_P ( ( unsigned char * ) TasmotaGlobal . serial_in_buffer , TasmotaGlobal . serial_in_byte_counter , hex_char , sizeof ( hex_char ) ) ) ;
2020-05-24 16:15:06 +01:00
} else {
2020-10-30 11:29:48 +00:00
ResponseAppend_P ( EscapeJSONString ( TasmotaGlobal . serial_in_buffer ) . c_str ( ) ) ;
2020-05-24 16:15:06 +01:00
}
ResponseAppend_P ( PSTR ( " \" " ) ) ;
}
2020-05-26 11:35:21 +01:00
ResponseJsonEnd ( ) ;
2020-05-24 16:15:06 +01:00
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_SERIALRECEIVED ) ) ;
2020-10-29 11:21:24 +00:00
TasmotaGlobal . serial_in_byte_counter = 0 ;
2019-12-02 09:44:27 +00:00
}
}
2022-05-06 13:57:03 +01:00
/********************************************************************************************/
# ifdef ESP32
String console_buffer = " " ;
void TasConsoleInput ( void ) {
static bool console_buffer_overrun = false ;
while ( TasConsole . available ( ) ) {
delay ( 0 ) ;
char console_in_byte = TasConsole . read ( ) ;
if ( isprint ( console_in_byte ) ) { // Any char between 32 and 127
if ( console_buffer . length ( ) < INPUT_BUFFER_SIZE ) { // Add char to string if it still fits
console_buffer + = console_in_byte ;
} else {
console_buffer_overrun = true ; // Signal overrun but continue reading input to flush until '\n' (EOL)
}
}
2022-06-06 16:48:40 +01:00
else if ( console_in_byte = = ' \n ' ) {
2022-05-06 13:57:03 +01:00
TasmotaGlobal . seriallog_level = ( Settings - > seriallog_level < LOG_LEVEL_INFO ) ? ( uint8_t ) LOG_LEVEL_INFO : Settings - > seriallog_level ;
if ( console_buffer_overrun ) {
2022-06-07 09:35:51 +01:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_COMMAND " USB buffer overrun " ) ) ;
2022-05-06 13:57:03 +01:00
} else {
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_COMMAND " %s " ) , console_buffer . c_str ( ) ) ;
2022-06-07 09:35:51 +01:00
ExecuteCommand ( console_buffer . c_str ( ) , SRC_USBCONSOLE ) ;
2022-05-06 13:57:03 +01:00
}
console_buffer = " " ;
console_buffer_overrun = false ;
TasConsole . flush ( ) ;
return ;
}
}
}
# endif // ESP32
2020-03-22 16:42:32 +00:00
/********************************************************************************************/
2021-11-16 21:21:44 +00:00
# ifdef ESP32
// Since ESP-IDF 4.4, GPIO matrix or I/O is not reset during a restart
// and GPIO configuration can get stuck because of leftovers
//
// This patched version of pinMode forces a full GPIO reset before setting new mode
//
extern " C " void ARDUINO_ISR_ATTR __pinMode ( uint8_t pin , uint8_t mode ) ;
extern " C " void ARDUINO_ISR_ATTR pinMode ( uint8_t pin , uint8_t mode ) {
gpio_reset_pin ( ( gpio_num_t ) pin ) ;
__pinMode ( pin , mode ) ;
}
# endif
/********************************************************************************************/
2019-12-02 09:44:27 +00:00
void GpioInit ( void )
{
2021-06-11 17:14:12 +01:00
if ( ! ValidModule ( Settings - > module ) ) {
2019-12-02 09:44:27 +00:00
uint32_t module = MODULE ;
2020-04-10 17:24:08 +01:00
if ( ! ValidModule ( MODULE ) ) {
# ifdef ESP8266
module = SONOFF_BASIC ;
# endif // ESP8266
# ifdef ESP32
module = WEMOS ;
# endif // ESP32
}
2021-06-11 17:14:12 +01:00
Settings - > module = module ;
Settings - > last_module = module ;
2019-12-02 09:44:27 +00:00
}
SetModuleType ( ) ;
2021-01-23 15:26:23 +00:00
// AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: Used GPIOs %d"), GPIO_SENSOR_END);
2020-08-23 14:34:19 +01:00
2020-09-25 17:34:14 +01:00
# ifdef ESP8266
2020-09-29 13:08:48 +01:00
ConvertGpios ( ) ;
2020-09-25 17:34:14 +01:00
# endif // ESP8266
2020-09-25 17:15:31 +01:00
2021-06-11 17:14:12 +01:00
for ( uint32_t i = 0 ; i < nitems ( Settings - > user_template . gp . io ) ; i + + ) {
if ( ( Settings - > user_template . gp . io [ i ] > = AGPIO ( GPIO_SENSOR_END ) ) & & ( Settings - > user_template . gp . io [ i ] < AGPIO ( GPIO_USER ) ) ) {
Settings - > user_template . gp . io [ i ] = AGPIO ( GPIO_USER ) ; // Fix not supported sensor ids in template
2019-12-02 09:44:27 +00:00
}
}
2020-11-24 14:14:22 +00:00
myio template_gp ;
TemplateGpios ( & template_gp ) ;
2021-06-11 17:14:12 +01:00
for ( uint32_t i = 0 ; i < nitems ( Settings - > my_gp . io ) ; i + + ) {
if ( ( Settings - > my_gp . io [ i ] > = AGPIO ( GPIO_SENSOR_END ) ) & & ( Settings - > my_gp . io [ i ] < AGPIO ( GPIO_USER ) ) ) {
Settings - > my_gp . io [ i ] = GPIO_NONE ; // Fix not supported sensor ids in module
2019-12-02 09:44:27 +00:00
}
2021-06-11 17:14:12 +01:00
else if ( Settings - > my_gp . io [ i ] > GPIO_NONE ) {
TasmotaGlobal . my_module . io [ i ] = Settings - > my_gp . io [ i ] ; // Set User selected Module sensors
2019-12-02 09:44:27 +00:00
}
2020-11-24 14:14:22 +00:00
if ( ( template_gp . io [ i ] > GPIO_NONE ) & & ( template_gp . io [ i ] < AGPIO ( GPIO_USER ) ) ) {
TasmotaGlobal . my_module . io [ i ] = template_gp . io [ i ] ; // Force Template override
2019-12-02 09:44:27 +00:00
}
}
2021-02-28 11:50:02 +00:00
for ( uint32_t i = 0 ; i < nitems ( TasmotaGlobal . my_module . io ) ; i + + ) {
2020-10-30 11:29:48 +00:00
uint32_t mpin = ValidPin ( i , TasmotaGlobal . my_module . io [ i ] ) ;
2019-12-02 09:44:27 +00:00
DEBUG_CORE_LOG ( PSTR ( " INI: gpio pin %d, mpin %d " ) , i , mpin ) ;
2020-04-28 13:42:47 +01:00
if ( mpin ) { // Above GPIO_NONE
2019-12-02 09:44:27 +00:00
XdrvMailbox . index = mpin ;
XdrvMailbox . payload = i ;
2020-12-20 12:22:01 +00:00
if ( ( mpin > = AGPIO ( GPIO_OPTION_A ) ) & & ( mpin < ( AGPIO ( GPIO_OPTION_A ) + MAX_OPTIONS_A ) ) ) {
bitSet ( TasmotaGlobal . gpio_optiona . data , mpin - AGPIO ( GPIO_OPTION_A ) ) ;
mpin = GPIO_NONE ;
}
2021-01-06 11:51:12 +00:00
# ifdef ROTARY_V1
else if ( ( mpin > = AGPIO ( GPIO_ROT1A_NP ) ) & & ( mpin < ( AGPIO ( GPIO_ROT1A_NP ) + MAX_ROTARIES ) ) ) {
RotaryAPullupFlag ( mpin - AGPIO ( GPIO_ROT1A_NP ) ) ;
mpin - = ( AGPIO ( GPIO_ROT1A_NP ) - AGPIO ( GPIO_ROT1A ) ) ;
}
else if ( ( mpin > = AGPIO ( GPIO_ROT1B_NP ) ) & & ( mpin < ( AGPIO ( GPIO_ROT1B_NP ) + MAX_ROTARIES ) ) ) {
RotaryBPullupFlag ( mpin - AGPIO ( GPIO_ROT1B_NP ) ) ;
mpin - = ( AGPIO ( GPIO_ROT1B_NP ) - AGPIO ( GPIO_ROT1B ) ) ;
}
# endif // ROTARY_V1
2020-12-20 12:22:01 +00:00
else if ( ( mpin > = AGPIO ( GPIO_SWT1_NP ) ) & & ( mpin < ( AGPIO ( GPIO_SWT1_NP ) + MAX_SWITCHES ) ) ) {
2020-04-29 16:44:03 +01:00
SwitchPullupFlag ( mpin - AGPIO ( GPIO_SWT1_NP ) ) ;
mpin - = ( AGPIO ( GPIO_SWT1_NP ) - AGPIO ( GPIO_SWT1 ) ) ;
2019-12-02 09:44:27 +00:00
}
2020-04-29 16:44:03 +01:00
else if ( ( mpin > = AGPIO ( GPIO_KEY1_NP ) ) & & ( mpin < ( AGPIO ( GPIO_KEY1_NP ) + MAX_KEYS ) ) ) {
ButtonPullupFlag ( mpin - AGPIO ( GPIO_KEY1_NP ) ) ; // 0 .. 3
mpin - = ( AGPIO ( GPIO_KEY1_NP ) - AGPIO ( GPIO_KEY1 ) ) ;
2019-12-02 09:44:27 +00:00
}
2020-04-29 16:44:03 +01:00
else if ( ( mpin > = AGPIO ( GPIO_KEY1_INV ) ) & & ( mpin < ( AGPIO ( GPIO_KEY1_INV ) + MAX_KEYS ) ) ) {
ButtonInvertFlag ( mpin - AGPIO ( GPIO_KEY1_INV ) ) ; // 0 .. 3
mpin - = ( AGPIO ( GPIO_KEY1_INV ) - AGPIO ( GPIO_KEY1 ) ) ;
2019-12-02 09:44:27 +00:00
}
2020-04-29 16:44:03 +01:00
else if ( ( mpin > = AGPIO ( GPIO_KEY1_INV_NP ) ) & & ( mpin < ( AGPIO ( GPIO_KEY1_INV_NP ) + MAX_KEYS ) ) ) {
ButtonPullupFlag ( mpin - AGPIO ( GPIO_KEY1_INV_NP ) ) ; // 0 .. 3
ButtonInvertFlag ( mpin - AGPIO ( GPIO_KEY1_INV_NP ) ) ; // 0 .. 3
mpin - = ( AGPIO ( GPIO_KEY1_INV_NP ) - AGPIO ( GPIO_KEY1 ) ) ;
2019-12-02 09:44:27 +00:00
}
2020-05-27 14:59:32 +01:00
# ifdef ESP32
2022-01-07 15:17:53 +00:00
else if ( ( mpin > = AGPIO ( GPIO_OPTION_E ) ) & & ( mpin < ( AGPIO ( GPIO_OPTION_E ) + MAX_OPTIONS_E ) ) ) {
2022-02-13 10:22:10 +00:00
TasmotaGlobal . emulated_module_type = pgm_read_byte ( kModuleEmulationList + ( mpin - AGPIO ( GPIO_OPTION_E ) ) ) ;
2022-01-07 15:17:53 +00:00
SetModuleType ( ) ;
mpin = GPIO_NONE ;
}
2021-04-27 10:36:10 +01:00
else if ( ( mpin > = AGPIO ( GPIO_SWT1_PD ) ) & & ( mpin < ( AGPIO ( GPIO_SWT1_PD ) + MAX_SWITCHES ) ) ) {
SwitchPulldownFlag ( mpin - AGPIO ( GPIO_SWT1_PD ) ) ;
mpin - = ( AGPIO ( GPIO_SWT1_PD ) - AGPIO ( GPIO_SWT1 ) ) ;
}
else if ( ( mpin > = AGPIO ( GPIO_KEY1_PD ) ) & & ( mpin < ( AGPIO ( GPIO_KEY1_PD ) + MAX_KEYS ) ) ) {
ButtonPulldownFlag ( mpin - AGPIO ( GPIO_KEY1_PD ) ) ; // 0 .. 3
mpin - = ( AGPIO ( GPIO_KEY1_PD ) - AGPIO ( GPIO_KEY1 ) ) ;
}
2021-04-26 12:56:44 +01:00
else if ( ( mpin > = AGPIO ( GPIO_KEY1_INV_PD ) ) & & ( mpin < ( AGPIO ( GPIO_KEY1_INV_PD ) + MAX_KEYS ) ) ) {
ButtonPulldownFlag ( mpin - AGPIO ( GPIO_KEY1_INV_PD ) ) ; // 0 .. 3
ButtonInvertFlag ( mpin - AGPIO ( GPIO_KEY1_INV_PD ) ) ; // 0 .. 3
mpin - = ( AGPIO ( GPIO_KEY1_INV_PD ) - AGPIO ( GPIO_KEY1 ) ) ;
}
2020-05-27 14:59:32 +01:00
else if ( ( mpin > = AGPIO ( GPIO_KEY1_TC ) ) & & ( mpin < ( AGPIO ( GPIO_KEY1_TC ) + MAX_KEYS ) ) ) {
ButtonTouchFlag ( mpin - AGPIO ( GPIO_KEY1_TC ) ) ; // 0 .. 3
mpin - = ( AGPIO ( GPIO_KEY1_TC ) - AGPIO ( GPIO_KEY1 ) ) ;
}
# endif //ESP32
2020-04-29 16:44:03 +01:00
else if ( ( mpin > = AGPIO ( GPIO_REL1_INV ) ) & & ( mpin < ( AGPIO ( GPIO_REL1_INV ) + MAX_RELAYS ) ) ) {
2020-10-28 18:03:39 +00:00
bitSet ( TasmotaGlobal . rel_inverted , mpin - AGPIO ( GPIO_REL1_INV ) ) ;
2020-04-29 16:44:03 +01:00
mpin - = ( AGPIO ( GPIO_REL1_INV ) - AGPIO ( GPIO_REL1 ) ) ;
2019-12-02 09:44:27 +00:00
}
2020-04-29 16:44:03 +01:00
else if ( ( mpin > = AGPIO ( GPIO_LED1_INV ) ) & & ( mpin < ( AGPIO ( GPIO_LED1_INV ) + MAX_LEDS ) ) ) {
2020-10-30 11:29:48 +00:00
bitSet ( TasmotaGlobal . led_inverted , mpin - AGPIO ( GPIO_LED1_INV ) ) ;
2020-04-29 16:44:03 +01:00
mpin - = ( AGPIO ( GPIO_LED1_INV ) - AGPIO ( GPIO_LED1 ) ) ;
2019-12-02 09:44:27 +00:00
}
2020-04-29 16:44:03 +01:00
else if ( mpin = = AGPIO ( GPIO_LEDLNK_INV ) ) {
2020-10-30 11:29:48 +00:00
TasmotaGlobal . ledlnk_inverted = 1 ;
2020-04-29 16:44:03 +01:00
mpin - = ( AGPIO ( GPIO_LEDLNK_INV ) - AGPIO ( GPIO_LEDLNK ) ) ;
2019-12-02 09:44:27 +00:00
}
2021-11-03 10:58:05 +00:00
else if ( mpin = = AGPIO ( GPIO_HEARTBEAT_INV ) ) {
TasmotaGlobal . heartbeat_inverted = 1 ;
mpin - = ( AGPIO ( GPIO_HEARTBEAT_INV ) - AGPIO ( GPIO_HEARTBEAT ) ) ;
}
2020-04-29 16:44:03 +01:00
else if ( ( mpin > = AGPIO ( GPIO_PWM1_INV ) ) & & ( mpin < ( AGPIO ( GPIO_PWM1_INV ) + MAX_PWMS ) ) ) {
2022-01-27 20:30:05 +00:00
bitSet ( TasmotaGlobal . pwm_inverted , mpin - AGPIO ( GPIO_PWM1_INV ) ) ; // PWMi are later converted to PMW, but marked as inverted in TasmotaGlobal.pwm_inverted
2020-04-29 16:44:03 +01:00
mpin - = ( AGPIO ( GPIO_PWM1_INV ) - AGPIO ( GPIO_PWM1 ) ) ;
2019-12-02 09:44:27 +00:00
}
else if ( XdrvCall ( FUNC_PIN_STATE ) ) {
mpin = XdrvMailbox . index ;
}
else if ( XsnsCall ( FUNC_PIN_STATE ) ) {
mpin = XdrvMailbox . index ;
} ;
}
2020-04-28 13:42:47 +01:00
if ( mpin ) { SetPin ( i , mpin ) ; } // Anything above GPIO_NONE and below GPIO_SENSOR_END
2019-12-02 09:44:27 +00:00
}
2021-02-28 11:50:02 +00:00
// AddLogBufferSize(LOG_LEVEL_DEBUG, (uint8_t*)TasmotaGlobal.gpio_pin, nitems(TasmotaGlobal.gpio_pin), sizeof(TasmotaGlobal.gpio_pin[0]));
2020-04-28 13:42:47 +01:00
2021-06-11 17:14:12 +01:00
analogWriteRange ( Settings - > pwm_range ) ; // Default is 1023 (Arduino.h)
analogWriteFreq ( Settings - > pwm_frequency ) ; // Default is 1000 (core_esp8266_wiring_pwm.c)
2019-12-02 09:44:27 +00:00
2020-08-03 10:52:25 +01:00
# ifdef ESP8266
2020-10-30 11:29:48 +00:00
if ( ( 2 = = Pin ( GPIO_TXD ) ) | | ( H801 = = TasmotaGlobal . module_type ) ) { Serial . set_tx ( 2 ) ; }
2021-09-20 13:31:00 +01:00
SetSerialSwap ( ) ;
2020-12-29 16:42:53 +00:00
# endif
2020-12-31 15:17:30 +00:00
uint32_t sspi_mosi = ( PinUsed ( GPIO_SSPI_SCLK ) & & PinUsed ( GPIO_SSPI_MOSI ) ) ? SPI_MOSI : SPI_NONE ;
uint32_t sspi_miso = ( PinUsed ( GPIO_SSPI_SCLK ) & & PinUsed ( GPIO_SSPI_MISO ) ) ? SPI_MISO : SPI_NONE ;
TasmotaGlobal . soft_spi_enabled = sspi_mosi + sspi_miso ;
2021-01-09 07:50:29 +00:00
AddLogSpi ( 0 , Pin ( GPIO_SSPI_SCLK ) , Pin ( GPIO_SSPI_MOSI ) , Pin ( GPIO_SSPI_MISO ) ) ;
2020-08-03 10:52:25 +01:00
2019-12-02 09:44:27 +00:00
# ifdef USE_SPI
2020-12-29 16:42:53 +00:00
# ifdef ESP8266
if ( ! TasmotaGlobal . soft_spi_enabled ) {
2021-01-01 14:05:58 +00:00
bool valid_cs = ( ValidSpiPinUsed ( GPIO_SPI_CS ) | |
ValidSpiPinUsed ( GPIO_RC522_CS ) | |
2021-01-15 15:19:56 +00:00
( ValidSpiPinUsed ( GPIO_NRF24_CS ) & & ValidSpiPinUsed ( GPIO_NRF24_DC ) ) | |
2021-02-13 06:43:59 +00:00
ValidSpiPinUsed ( GPIO_ILI9341_CS ) | |
ValidSpiPinUsed ( GPIO_ILI9341_DC ) | | // there are also boards without cs
2021-01-01 14:05:58 +00:00
ValidSpiPinUsed ( GPIO_EPAPER29_CS ) | |
ValidSpiPinUsed ( GPIO_EPAPER42_CS ) | |
ValidSpiPinUsed ( GPIO_ILI9488_CS ) | |
ValidSpiPinUsed ( GPIO_SSD1351_CS ) | |
ValidSpiPinUsed ( GPIO_RA8876_CS ) | |
ValidSpiPinUsed ( GPIO_ST7789_DC ) | | // ST7789 CS may be omitted so chk DC too
2021-01-01 15:05:06 +00:00
ValidSpiPinUsed ( GPIO_ST7789_CS ) | |
2021-01-15 15:19:56 +00:00
( ValidSpiPinUsed ( GPIO_SSD1331_CS ) & & ValidSpiPinUsed ( GPIO_SSD1331_DC ) ) | |
2021-07-12 16:47:04 +01:00
ValidSpiPinUsed ( GPIO_SDCARD_CS ) | |
ValidSpiPinUsed ( GPIO_MCP2515_CS )
2020-12-31 15:17:30 +00:00
) ;
// If SPI_CS and/or SPI_DC is used they must be valid
2021-01-15 15:19:56 +00:00
TasmotaGlobal . spi_enabled = ( valid_cs ) ? SPI_MOSI_MISO : SPI_NONE ;
2020-12-29 16:42:53 +00:00
if ( TasmotaGlobal . spi_enabled ) {
TasmotaGlobal . my_module . io [ 12 ] = AGPIO ( GPIO_SPI_MISO ) ;
SetPin ( 12 , AGPIO ( GPIO_SPI_MISO ) ) ;
TasmotaGlobal . my_module . io [ 13 ] = AGPIO ( GPIO_SPI_MOSI ) ;
SetPin ( 13 , AGPIO ( GPIO_SPI_MOSI ) ) ;
TasmotaGlobal . my_module . io [ 14 ] = AGPIO ( GPIO_SPI_CLK ) ;
SetPin ( 14 , AGPIO ( GPIO_SPI_CLK ) ) ;
}
2019-12-02 09:44:27 +00:00
}
2020-11-28 11:46:17 +00:00
# endif // ESP8266
# ifdef ESP32
2022-02-03 14:35:14 +00:00
/*
2020-12-31 15:17:30 +00:00
if ( PinUsed ( GPIO_SPI_CS ) | |
PinUsed ( GPIO_RC522_CS ) | |
PinUsed ( GPIO_NRF24_CS ) | |
PinUsed ( GPIO_ILI9341_CS ) | |
PinUsed ( GPIO_EPAPER29_CS ) | |
PinUsed ( GPIO_EPAPER42_CS ) | |
PinUsed ( GPIO_ILI9488_CS ) | |
PinUsed ( GPIO_SSD1351_CS ) | |
PinUsed ( GPIO_RA8876_CS ) | |
2021-01-01 14:05:58 +00:00
PinUsed ( GPIO_ST7789_DC ) | | // ST7789 CS may be omitted so chk DC too
2021-01-01 15:05:06 +00:00
PinUsed ( GPIO_ST7789_CS ) | |
2021-01-04 15:45:46 +00:00
PinUsed ( GPIO_SSD1331_CS ) | |
PinUsed ( GPIO_SDCARD_CS )
2020-12-31 15:17:30 +00:00
) {
uint32_t spi_mosi = ( PinUsed ( GPIO_SPI_CLK ) & & PinUsed ( GPIO_SPI_MOSI ) ) ? SPI_MOSI : SPI_NONE ;
uint32_t spi_miso = ( PinUsed ( GPIO_SPI_CLK ) & & PinUsed ( GPIO_SPI_MISO ) ) ? SPI_MISO : SPI_NONE ;
TasmotaGlobal . spi_enabled = spi_mosi + spi_miso ;
2020-06-20 16:58:21 +01:00
}
2022-02-03 14:35:14 +00:00
*/
uint32_t spi_mosi = ( PinUsed ( GPIO_SPI_CLK ) & & PinUsed ( GPIO_SPI_MOSI ) ) ? SPI_MOSI : SPI_NONE ;
uint32_t spi_miso = ( PinUsed ( GPIO_SPI_CLK ) & & PinUsed ( GPIO_SPI_MISO ) ) ? SPI_MISO : SPI_NONE ;
TasmotaGlobal . spi_enabled = spi_mosi + spi_miso ;
2020-11-28 11:46:17 +00:00
# endif // ESP32
2020-12-31 15:17:30 +00:00
AddLogSpi ( 1 , Pin ( GPIO_SPI_CLK ) , Pin ( GPIO_SPI_MOSI ) , Pin ( GPIO_SPI_MISO ) ) ;
2020-12-29 16:42:53 +00:00
# endif // USE_SPI
2019-12-02 09:44:27 +00:00
2021-02-28 11:50:02 +00:00
for ( uint32_t i = 0 ; i < nitems ( TasmotaGlobal . my_module . io ) ; i + + ) {
2020-10-30 11:29:48 +00:00
uint32_t mpin = ValidPin ( i , TasmotaGlobal . my_module . io [ i ] ) ;
2021-01-23 15:26:23 +00:00
// AddLog(LOG_LEVEL_DEBUG, PSTR("INI: gpio pin %d, mpin %d"), i, mpin);
2020-10-23 14:18:58 +01:00
if ( AGPIO ( GPIO_OUTPUT_HI ) = = mpin ) {
2022-05-19 21:35:59 +01:00
pinMode ( i , OUTPUT ) ;
digitalWrite ( i , 1 ) ;
2020-10-23 14:18:58 +01:00
}
else if ( AGPIO ( GPIO_OUTPUT_LO ) = = mpin ) {
2022-05-19 21:35:59 +01:00
pinMode ( i , OUTPUT ) ;
digitalWrite ( i , 0 ) ;
2020-10-23 14:18:58 +01:00
}
2021-07-26 16:10:08 +01:00
/*
// Until 20210726
2020-10-23 14:18:58 +01:00
// Set any non-used GPIO to INPUT - Related to resetPins() in support_legacy_cores.ino
// Doing it here solves relay toggles at restart.
2021-06-08 19:31:01 +01:00
# if CONFIG_IDF_TARGET_ESP32C3
else if ( ( ( i < 11 ) | | ( i > 17 ) ) & & ( GPIO_NONE = = mpin ) ) { // Skip SPI flash interface
if ( ! ( ( 20 = = i ) | | ( 21 = = i ) ) ) { // Skip serial
pinMode ( i , INPUT ) ;
}
}
# else // CONFIG_IDF_TARGET_ESP32C3
2020-10-23 14:18:58 +01:00
else if ( ( ( i < 6 ) | | ( i > 11 ) ) & & ( GPIO_NONE = = mpin ) ) { // Skip SPI flash interface
2020-03-25 10:29:46 +00:00
if ( ! ( ( 1 = = i ) | | ( 3 = = i ) ) ) { // Skip serial
pinMode ( i , INPUT ) ;
}
}
2021-06-08 19:31:01 +01:00
# endif // CONFIG_IDF_TARGET_ESP32C3
2021-07-26 16:10:08 +01:00
*/
# ifdef ESP8266
// Set any non-used GPIO to INPUT - Related to resetPins() in support_legacy_cores.ino
// Doing it here solves relay toggles at restart.
else if ( ( ( i < 6 ) | | ( i > 11 ) ) & & ( GPIO_NONE = = mpin ) ) { // Skip SPI flash interface
if ( ! ( ( 1 = = i ) | | ( 3 = = i ) ) ) { // Skip serial
pinMode ( i , INPUT ) ;
}
}
# endif // ESP8266
2020-03-25 10:29:46 +00:00
}
2022-05-20 10:22:49 +01:00
DigitalWrite ( GPIO_HEARTBEAT , 0 , TasmotaGlobal . heartbeat_inverted ) ;
2021-11-03 10:58:05 +00:00
2021-05-07 10:51:22 +01:00
// Digital input
for ( uint32_t i = 0 ; i < MAX_SWITCHES ; i + + ) {
if ( PinUsed ( GPIO_INPUT , i ) ) {
pinMode ( Pin ( GPIO_INPUT , i ) , INPUT ) ;
}
}
2019-12-02 09:44:27 +00:00
# ifdef USE_I2C
2020-10-30 11:29:48 +00:00
TasmotaGlobal . i2c_enabled = ( PinUsed ( GPIO_I2C_SCL ) & & PinUsed ( GPIO_I2C_SDA ) ) ;
if ( TasmotaGlobal . i2c_enabled ) {
2021-10-29 17:49:29 +01:00
TasmotaGlobal . i2c_enabled = I2cBegin ( Pin ( GPIO_I2C_SDA ) , Pin ( GPIO_I2C_SCL ) ) ;
2019-12-02 09:44:27 +00:00
}
2021-03-10 21:20:21 +00:00
# ifdef ESP32
TasmotaGlobal . i2c_enabled_2 = ( PinUsed ( GPIO_I2C_SCL , 1 ) & & PinUsed ( GPIO_I2C_SDA , 1 ) ) ;
if ( TasmotaGlobal . i2c_enabled_2 ) {
2021-10-29 17:49:29 +01:00
TasmotaGlobal . i2c_enabled_2 = I2c2Begin ( Pin ( GPIO_I2C_SDA , 1 ) , Pin ( GPIO_I2C_SCL , 1 ) ) ;
2021-03-10 21:20:21 +00:00
}
# endif
2019-12-02 09:44:27 +00:00
# endif // USE_I2C
2020-10-30 11:29:48 +00:00
TasmotaGlobal . devices_present = 0 ;
TasmotaGlobal . light_type = LT_BASIC ; // Use basic PWM control if SetOption15 = 0
2020-10-06 11:12:14 +01:00
XsnsCall ( FUNC_MODULE_INIT ) ;
2019-12-02 09:44:27 +00:00
if ( XdrvCall ( FUNC_MODULE_INIT ) ) {
// Serviced
}
2020-04-10 17:24:08 +01:00
# ifdef ESP8266
2020-10-30 11:29:48 +00:00
else if ( YTF_IR_BRIDGE = = TasmotaGlobal . module_type ) {
2019-12-02 09:44:27 +00:00
ClaimSerial ( ) ; // Stop serial loopback mode
2020-10-30 11:29:48 +00:00
// TasmotaGlobal.devices_present = 1;
2019-12-02 09:44:27 +00:00
}
2020-10-30 11:29:48 +00:00
else if ( SONOFF_DUAL = = TasmotaGlobal . module_type ) {
TasmotaGlobal . devices_present = 2 ;
2019-12-29 12:27:48 +00:00
SetSerial ( 19200 , TS_SERIAL_8N1 ) ;
2019-12-02 09:44:27 +00:00
}
2020-10-30 11:29:48 +00:00
else if ( CH4 = = TasmotaGlobal . module_type ) {
TasmotaGlobal . devices_present = 4 ;
2019-12-29 12:27:48 +00:00
SetSerial ( 19200 , TS_SERIAL_8N1 ) ;
2019-12-02 09:44:27 +00:00
}
# ifdef USE_SONOFF_SC
2020-10-30 11:29:48 +00:00
else if ( SONOFF_SC = = TasmotaGlobal . module_type ) {
2019-12-29 12:27:48 +00:00
SetSerial ( 19200 , TS_SERIAL_8N1 ) ;
2019-12-02 09:44:27 +00:00
}
# endif // USE_SONOFF_SC
2020-04-10 17:24:08 +01:00
# endif // ESP8266
2019-12-02 09:44:27 +00:00
2022-01-27 20:30:05 +00:00
GpioInitPwm ( ) ;
2019-12-10 21:00:38 +00:00
2019-12-02 09:44:27 +00:00
for ( uint32_t i = 0 ; i < MAX_RELAYS ; i + + ) {
2020-04-27 11:54:07 +01:00
if ( PinUsed ( GPIO_REL1 , i ) ) {
2020-10-30 11:29:48 +00:00
TasmotaGlobal . devices_present + + ;
2020-04-10 17:24:08 +01:00
# ifdef ESP8266
2020-10-30 11:29:48 +00:00
if ( EXS_RELAY = = TasmotaGlobal . module_type ) {
if ( i & 1 ) { TasmotaGlobal . devices_present - - ; }
2019-12-02 09:44:27 +00:00
}
2020-04-10 17:24:08 +01:00
# endif // ESP8266
2019-12-02 09:44:27 +00:00
}
}
for ( uint32_t i = 0 ; i < MAX_LEDS ; i + + ) {
2020-04-27 11:54:07 +01:00
if ( PinUsed ( GPIO_LED1 , i ) ) {
2019-12-02 09:44:27 +00:00
# ifdef USE_ARILUX_RF
2020-10-30 11:29:48 +00:00
if ( ( 3 = = i ) & & ( TasmotaGlobal . leds_present < 2 ) & & ! PinUsed ( GPIO_ARIRFSEL ) ) {
2020-06-20 16:58:21 +01:00
SetPin ( Pin ( GPIO_LED1 , i ) , AGPIO ( GPIO_ARIRFSEL ) ) ; // Legacy support where LED4 was Arilux RF enable
2019-12-02 09:44:27 +00:00
} else {
# endif
2020-10-30 11:29:48 +00:00
TasmotaGlobal . leds_present + + ;
2022-05-19 17:02:05 +01:00
DigitalWrite ( GPIO_LED1 , i , bitRead ( TasmotaGlobal . led_inverted , i ) ) ;
2019-12-02 09:44:27 +00:00
# ifdef USE_ARILUX_RF
}
# endif
}
}
2022-05-20 10:22:49 +01:00
DigitalWrite ( GPIO_LEDLNK , 0 , TasmotaGlobal . ledlnk_inverted ) ;
2019-12-02 09:44:27 +00:00
2020-03-13 17:08:44 +00:00
# ifdef USE_PWM_DIMMER
2020-10-30 11:29:48 +00:00
if ( PWM_DIMMER = = TasmotaGlobal . module_type & & PinUsed ( GPIO_REL1 ) ) { TasmotaGlobal . devices_present - - ; }
2020-03-13 17:08:44 +00:00
# endif // USE_PWM_DIMMER
2021-06-11 17:14:12 +01:00
SetLedPower ( Settings - > ledstate & 8 ) ;
SetLedLink ( Settings - > ledstate & 8 ) ;
2019-12-02 09:44:27 +00:00
}