2019-12-02 09:44:27 +00:00
/*
support_tasmota . ino - Core support for Tasmota
2019-12-31 13:23:34 +00:00
Copyright ( C ) 2020 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/>.
*/
const char kSleepMode [ ] PROGMEM = " Dynamic|Normal " ;
const char kPrefixes [ ] PROGMEM = D_CMND " | " D_STAT " | " D_TELE ;
char * Format ( char * output , const char * input , int size )
{
char * token ;
uint32_t digits = 0 ;
if ( strstr ( input , " % " ) ! = nullptr ) {
strlcpy ( output , input , size ) ;
token = strtok ( output , " % " ) ;
if ( strstr ( input , " % " ) = = input ) {
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 {
snprintf_P ( tmp , size , PSTR ( " %s%c0%dX " ) , output , ' % ' , digits ) ;
2020-04-13 16:45:06 +01:00
snprintf_P ( output , size , tmp , ESP_getChipId ( ) ) ; // %06X - full chip ID in hex
2019-12-02 09:44:27 +00:00
}
} else {
if ( strchr ( token , ' d ' ) ) {
2020-04-13 16:45:06 +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 ;
}
char * GetTopic_P ( char * stopic , uint32_t prefix , char * topic , const char * subtopic )
{
/* 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 ) ;
if ( fallback_topic_flag | | ( prefix > 3 ) ) {
bool fallback = ( prefix < 8 ) ;
prefix & = 3 ;
char stemp [ 11 ] ;
fulltopic = GetTextIndexed ( stemp , sizeof ( stemp ) , prefix , kPrefixes ) ;
fulltopic + = F ( " / " ) ;
if ( fallback ) {
fulltopic + = mqtt_client ;
fulltopic + = F ( " _fb " ) ; // cmnd/<mqttclient>_fb
} else {
fulltopic + = topic ; // cmnd/<grouptopic>
}
} 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 ) ) ;
2019-12-02 09:44:27 +00:00
fulltopic . replace ( FPSTR ( MQTT_TOKEN_TOPIC ) , topic ) ;
fulltopic . replace ( F ( " %hostname% " ) , my_hostname ) ;
String token_id = WiFi . macAddress ( ) ;
token_id . replace ( " : " , " " ) ;
fulltopic . replace ( F ( " %id% " ) , token_id ) ;
}
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>
2020-03-28 10:13:01 +00: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
}
/********************************************************************************************/
void SetLatchingRelay ( power_t lpower , uint32_t state )
{
// power xx00 - toggle REL1 (Off) and REL3 (Off) - device 1 Off, device 2 Off
// power xx01 - toggle REL2 (On) and REL3 (Off) - device 1 On, device 2 Off
// power xx10 - toggle REL1 (Off) and REL4 (On) - device 1 Off, device 2 On
// power xx11 - toggle REL2 (On) and REL4 (On) - device 1 On, device 2 On
if ( state & & ! latching_relay_pulse ) { // Set latching relay to power if previous pulse has finished
latching_power = lpower ;
latching_relay_pulse = 2 ; // max 200mS (initiated by stateloop())
}
for ( uint32_t i = 0 ; i < devices_present ; i + + ) {
uint32_t port = ( i < < 1 ) + ( ( latching_power > > i ) & 1 ) ;
2020-04-27 09:35:38 +01:00
DigitalWrite ( GPIO_REL1 , port , bitRead ( rel_inverted , port ) ? ! state : state ) ;
2019-12-02 09:44:27 +00:00
}
}
void SetDevicePower ( power_t rpower , uint32_t source )
{
ShowSource ( source ) ;
last_source = source ;
if ( POWER_ALL_ALWAYS_ON = = Settings . poweronstate ) { // All on and stay on
power = ( 1 < < devices_present ) - 1 ;
rpower = power ;
}
if ( Settings . flag . interlock ) { // Allow only one or no relay set - CMND_INTERLOCK - Enable/disable interlock
for ( uint32_t i = 0 ; i < MAX_INTERLOCKS ; i + + ) {
power_t mask = 1 ;
uint32_t count = 0 ;
for ( uint32_t j = 0 ; j < devices_present ; j + + ) {
if ( ( Settings . interlock [ i ] & mask ) & & ( rpower & mask ) ) {
count + + ;
}
mask < < = 1 ;
}
if ( count > 1 ) {
mask = ~ Settings . interlock [ i ] ; // Turn interlocked group off as there would be multiple relays on
power & = mask ;
rpower & = mask ;
}
}
}
if ( rpower ) { // Any power set
last_power = rpower ;
}
XdrvMailbox . index = rpower ;
XdrvCall ( FUNC_SET_POWER ) ; // Signal power state
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
2019-12-02 09:44:27 +00:00
else if ( ( SONOFF_DUAL = = my_module_type ) | | ( CH4 = = my_module_type ) ) {
Serial . write ( 0xA0 ) ;
Serial . write ( 0x04 ) ;
Serial . write ( rpower & 0xFF ) ;
Serial . write ( 0xA1 ) ;
Serial . write ( ' \n ' ) ;
Serial . flush ( ) ;
}
else if ( EXS_RELAY = = my_module_type ) {
SetLatchingRelay ( rpower , 1 ) ;
}
2020-04-10 17:24:08 +01:00
else
# endif // ESP8266
{
2019-12-02 09:44:27 +00:00
for ( uint32_t i = 0 ; i < devices_present ; i + + ) {
power_t state = rpower & 1 ;
2019-12-18 17:21:10 +00:00
if ( i < MAX_RELAYS ) {
2020-04-27 09:35:38 +01:00
DigitalWrite ( GPIO_REL1 , i , bitRead ( rel_inverted , i ) ? ! state : state ) ;
2019-12-02 09:44:27 +00:00
}
rpower > > = 1 ;
}
}
}
void RestorePower ( bool publish_power , uint32_t source )
{
if ( power ! = last_power ) {
2020-02-09 15:21:48 +00:00
power = last_power ;
SetDevicePower ( 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
// state 1 = POWER_ON = Relay On (turn off after Settings.pulse_timer * 100 mSec if enabled)
// 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 ) ) {
power_t all_on = ( 1 < < devices_present ) - 1 ;
switch ( state ) {
case POWER_OFF :
power = 0 ;
break ;
case POWER_ON :
power = all_on ;
break ;
case POWER_TOGGLE :
power ^ = all_on ; // Complement current state
}
SetDevicePower ( power , source ) ;
}
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-01-11 14:39:56 +00:00
if ( MOTOR = = my_module_type ) {
Settings . poweronstate = POWER_ALL_ON ; // Needs always on else in limbo!
}
2020-04-10 17:24:08 +01:00
# endif // ESP8266
2020-01-11 14:39:56 +00:00
if ( POWER_ALL_ALWAYS_ON = = Settings . poweronstate ) {
SetDevicePower ( 1 , SRC_RESTART ) ;
} else {
if ( ( ResetReason ( ) = = REASON_DEFAULT_RST ) | | ( ResetReason ( ) = = REASON_EXT_SYS_RST ) ) {
switch ( Settings . poweronstate ) {
case POWER_ALL_OFF :
case POWER_ALL_OFF_PULSETIME_ON :
power = 0 ;
SetDevicePower ( power , SRC_RESTART ) ;
break ;
case POWER_ALL_ON : // All on
power = ( 1 < < devices_present ) - 1 ;
SetDevicePower ( power , SRC_RESTART ) ;
break ;
case POWER_ALL_SAVED_TOGGLE :
power = ( Settings . power & ( ( 1 < < devices_present ) - 1 ) ) ^ POWER_MASK ;
if ( Settings . flag . save_state ) { // SetOption0 - Save power state and use after restart
SetDevicePower ( power , SRC_RESTART ) ;
}
break ;
case POWER_ALL_SAVED :
power = Settings . power & ( ( 1 < < devices_present ) - 1 ) ;
if ( Settings . flag . save_state ) { // SetOption0 - Save power state and use after restart
SetDevicePower ( power , SRC_RESTART ) ;
}
break ;
}
} else {
power = Settings . power & ( ( 1 < < devices_present ) - 1 ) ;
if ( Settings . flag . save_state ) { // SetOption0 - Save power state and use after restart
SetDevicePower ( power , SRC_RESTART ) ;
}
}
}
// Issue #526 and #909
for ( uint32_t i = 0 ; i < devices_present ; i + + ) {
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-04-26 16:33:27 +01:00
bitWrite ( power , i , digitalRead ( Pin ( GPIO_REL1 , i ) ) ^ bitRead ( rel_inverted , i ) ) ;
2020-01-11 14:39:56 +00:00
}
}
if ( ( i < MAX_PULSETIMERS ) & & ( bitRead ( power , i ) | | ( POWER_ALL_OFF_PULSETIME_ON = = Settings . poweronstate ) ) ) {
SetPulseTimer ( i , Settings . pulse_timer [ i ] ) ;
}
}
blink_powersave = power ;
}
2019-12-02 09:44:27 +00:00
void SetLedPowerIdx ( uint32_t led , uint32_t state )
{
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 ;
}
}
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 ;
led_power | = mask ;
} else {
led_power & = ( 0xFF ^ mask ) ;
}
2020-05-19 06:15:39 +01:00
uint16_t led_pwm_set = 0 ;
if ( bitRead ( led_inverted , led ) ) {
led_pwm_set = state ? Settings . pwm_range - Settings . ledpwm_on : Settings . pwm_range - Settings . ledpwm_off ;
} else {
led_pwm_set = state ? Settings . ledpwm_on : Settings . ledpwm_off ;
}
analogWrite ( led , led_pwm_set )
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
}
void SetLedPower ( uint32_t state )
{
2020-04-27 16:16:52 +01:00
if ( ! PinUsed ( GPIO_LEDLNK ) ) { // Legacy - Only use LED1 and/or LED2
2019-12-02 09:44:27 +00:00
SetLedPowerIdx ( 0 , state ) ;
} else {
power_t mask = 1 ;
for ( uint32_t i = 0 ; i < leds_present ; i + + ) { // Map leds to power
bool tstate = ( power & mask ) ;
SetLedPowerIdx ( i , tstate ) ;
mask < < = 1 ;
}
}
}
void SetLedPowerAll ( uint32_t state )
{
for ( uint32_t i = 0 ; i < leds_present ; i + + ) {
SetLedPowerIdx ( i , state ) ;
}
}
void SetLedLink ( uint32_t state )
{
2020-04-26 16:33:27 +01:00
uint32_t led_pin = Pin ( GPIO_LEDLNK ) ;
2019-12-02 09:44:27 +00:00
uint32_t led_inv = ledlnk_inverted ;
if ( 99 = = led_pin ) { // Legacy - LED1 is status
2020-04-26 16:33:27 +01:00
led_pin = Pin ( GPIO_LED1 ) ;
2019-12-02 09:44:27 +00:00
led_inv = bitRead ( led_inverted , 0 ) ;
}
if ( led_pin < 99 ) {
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
2019-12-02 09:44:27 +00:00
}
void SetPulseTimer ( uint32_t index , uint32_t time )
{
pulse_timer [ index ] = ( time > 111 ) ? millis ( ) + ( 1000 * ( time - 100 ) ) : ( time > 0 ) ? millis ( ) + ( 100 * time ) : 0L ;
}
uint32_t GetPulseTimer ( uint32_t index )
{
long time = TimePassedSince ( pulse_timer [ index ] ) ;
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
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 ) ) ;
if ( Settings . flag . mqtt_enabled & & MqttIsConnected ( ) & & ( strlen ( key_topic ) ! = 0 ) & & strcmp ( key_topic , " 0 " ) ) { // SetOption3 - Enable MQTT
if ( ! key & & ( device > devices_present ) ) {
device = 1 ; // Only allow number of buttons up to number of devices
}
GetTopic_P ( stopic , CMND , key_topic ,
GetPowerDevice ( scommand , device , sizeof ( scommand ) , ( key + Settings . flag . device_index_enable ) ) ) ; // cmnd/switchtopic/POWERx - SetOption26 - Switch between POWER or POWER1
if ( CLEAR_RETAIN = = state ) {
mqtt_data [ 0 ] = ' \0 ' ;
} else {
if ( ( Settings . flag3 . button_switch_force_local | | // SetOption61 - Force local operation when button/switch topic is set
! strcmp ( 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 ) ) {
state = ~ ( power > > ( device - 1 ) ) & 1 ; // POWER_OFF or POWER_ON
}
snprintf_P ( mqtt_data , sizeof ( mqtt_data ) , GetStateText ( state ) ) ;
}
# ifdef USE_DOMOTICZ
if ( ! ( DomoticzSendKey ( key , device , state , strlen ( mqtt_data ) ) ) ) {
# endif // USE_DOMOTICZ
2020-01-18 14:34:01 +00: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
result = ! Settings . flag3 . button_switch_force_local ; // SetOption61 - Force local operation when button/switch topic is set
} else {
Response_P ( PSTR ( " { \" %s%d \" :{ \" State \" :%d}} " ) , ( key ) ? " Switch " : " Button " , device , state ) ;
result = XdrvRulesProcess ( ) ;
}
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 ;
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
// state 1 = POWER_ON = Relay On (turn off after Settings.pulse_timer * 100 mSec if enabled)
// 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);
# ifdef USE_SONOFF_IFAN
if ( IsModuleIfan ( ) ) {
blink_mask & = 1 ; // No blinking on the fan relays
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 ;
}
# 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 ;
}
if ( ( device < 1 ) | | ( device > devices_present ) ) {
device = 1 ;
}
active_device = device ;
if ( device < = MAX_PULSETIMERS ) {
SetPulseTimer ( device - 1 , 0 ) ;
}
power_t mask = 1 < < ( device - 1 ) ; // Device to control
if ( state < = POWER_TOGGLE ) {
if ( ( blink_mask & mask ) ) {
blink_mask & = ( POWER_MASK ^ mask ) ; // Clear device mask
MqttPublishPowerBlinkState ( device ) ;
}
if ( Settings . flag . interlock & & // CMND_INTERLOCK - Enable/disable interlock
! interlock_mutex & &
( ( POWER_ON = = state ) | | ( ( POWER_TOGGLE = = state ) & & ! ( power & mask ) ) )
) {
interlock_mutex = true ; // Clear all but masked relay in interlock group if new set requested
for ( uint32_t i = 0 ; i < MAX_INTERLOCKS ; i + + ) {
if ( Settings . interlock [ i ] & mask ) { // Find interlock group
for ( uint32_t j = 0 ; j < devices_present ; j + + ) {
power_t imask = 1 < < j ;
if ( ( Settings . interlock [ i ] & imask ) & & ( power & imask ) & & ( mask ! = imask ) ) {
ExecuteCommandPower ( j + 1 , POWER_OFF , SRC_IGNORE ) ;
delay ( 50 ) ; // Add some delay to make sure never have more than one relay on
}
}
break ; // An interlocked relay is only present in one group so quit
}
}
interlock_mutex = false ;
}
switch ( state ) {
case POWER_OFF : {
power & = ( POWER_MASK ^ mask ) ;
break ; }
case POWER_ON :
power | = mask ;
break ;
case POWER_TOGGLE :
power ^ = mask ;
}
2020-02-21 15:09:21 +00:00
# ifdef USE_DEVICE_GROUPS
if ( SRC_REMOTE ! = source & & SRC_RETRY ! = source ) SendLocalDeviceGroupMessage ( DGR_MSGTYP_UPDATE , DGR_ITEM_POWER , power ) ;
# endif // USE_DEVICE_GROUPS
2019-12-02 09:44:27 +00:00
SetDevicePower ( power , source ) ;
# ifdef USE_DOMOTICZ
DomoticzUpdatePowerState ( device ) ;
# endif // USE_DOMOTICZ
# ifdef USE_KNX
KnxUpdatePowerState ( device , power ) ;
# endif // USE_KNX
if ( publish_power & & Settings . flag3 . hass_tele_on_power ) { // SetOption59 - Send tele/%topic%/STATE in addition to stat/%topic%/RESULT
MqttPublishTeleState ( ) ;
}
if ( device < = MAX_PULSETIMERS ) { // Restart PulseTime if powered On
SetPulseTimer ( device - 1 , ( ( ( POWER_ALL_OFF_PULSETIME_ON = = Settings . poweronstate ) ? ~ power : power ) & mask ) ? Settings . pulse_timer [ device - 1 ] : 0 ) ;
}
}
else if ( POWER_BLINK = = state ) {
if ( ! ( blink_mask & mask ) ) {
blink_powersave = ( blink_powersave & ( POWER_MASK ^ mask ) ) | ( power & mask ) ; // Save state
blink_power = ( power > > ( device - 1 ) ) & 1 ; // Prep to Toggle
}
blink_timer = millis ( ) + 100 ;
blink_counter = ( ( ! Settings . blinkcount ) ? 64000 : ( Settings . blinkcount * 2 ) ) + 1 ;
blink_mask | = mask ; // Set device mask
MqttPublishPowerBlinkState ( device ) ;
return ;
}
else if ( POWER_BLINK_STOP = = state ) {
bool flag = ( blink_mask & mask ) ;
blink_mask & = ( POWER_MASK ^ mask ) ; // Clear device mask
MqttPublishPowerBlinkState ( device ) ;
if ( flag ) {
ExecuteCommandPower ( device , ( blink_powersave > > ( device - 1 ) ) & 1 , SRC_IGNORE ) ; // Restore state
}
return ;
}
if ( publish_power ) {
MqttPublishPowerState ( device ) ;
}
}
void StopAllPowerBlink ( void )
{
power_t mask ;
for ( uint32_t i = 1 ; i < = devices_present ; i + + ) {
mask = 1 < < ( i - 1 ) ;
if ( blink_mask & mask ) {
blink_mask & = ( POWER_MASK ^ mask ) ; // Clear device mask
MqttPublishPowerBlinkState ( i ) ;
ExecuteCommandPower ( i , ( blink_powersave > > ( i - 1 ) ) & 1 , SRC_IGNORE ) ; // Restore state
}
}
}
void MqttShowPWMState ( void )
{
ResponseAppend_P ( PSTR ( " \" " D_CMND_PWM " \" :{ " ) ) ;
bool first = true ;
for ( uint32_t i = 0 ; i < MAX_PWMS ; i + + ) {
2020-04-27 11:54:07 +01:00
if ( PinUsed ( GPIO_PWM1 , i ) ) {
2019-12-02 09:44:27 +00:00
ResponseAppend_P ( PSTR ( " %s \" " D_CMND_PWM " %d \" :%d " ) , first ? " " : " , " , i + 1 , Settings . pwm_value [ i ] ) ;
first = false ;
}
}
ResponseJsonEnd ( ) ;
}
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 ( ) ) ;
# ifdef USE_ADC_VCC
dtostrfd ( ( double ) ESP . getVcc ( ) / 1000 , 3 , stemp1 ) ;
ResponseAppend_P ( PSTR ( " , \" " D_JSON_VCC " \" :%s " ) , stemp1 ) ;
# endif
ResponseAppend_P ( PSTR ( " , \" " D_JSON_HEAPSIZE " \" :%d, \" SleepMode \" : \" %s \" , \" Sleep \" :%u, \" LoadAvg \" :%u, \" MqttCount \" :%u " ) ,
2020-04-22 15:07:52 +01:00
ESP_getFreeHeap ( ) / 1024 , GetTextIndexed ( stemp1 , sizeof ( stemp1 ) , Settings . flag3 . sleep_normal , kSleepMode ) , // SetOption60 - Enable normal sleep instead of dynamic sleep
2020-04-10 17:24:08 +01:00
ssleep , loop_load_avg , MqttConnectCount ( ) ) ;
2019-12-02 09:44:27 +00:00
for ( uint32_t i = 1 ; i < = devices_present ; i + + ) {
# ifdef USE_LIGHT
if ( ( LightDevice ( ) ) & & ( i > = LightDevice ( ) ) ) {
if ( i = = LightDevice ( ) ) { LightState ( 1 ) ; } // call it only once
} else {
# endif
2019-12-06 23:10:04 +00:00
ResponseAppend_P ( PSTR ( " , \" %s \" : \" %s \" " ) , GetPowerDevice ( stemp1 , i , sizeof ( stemp1 ) , Settings . flag . device_index_enable ) , // SetOption26 - Switch between POWER or POWER1
2019-12-02 09:44:27 +00:00
GetStateText ( bitRead ( power , i - 1 ) ) ) ;
# ifdef USE_SONOFF_IFAN
if ( IsModuleIfan ( ) ) {
ResponseAppend_P ( PSTR ( " , \" " D_CMND_FANSPEED " \" :%d " ) , GetFanspeed ( ) ) ;
break ;
}
# endif // USE_SONOFF_IFAN
# ifdef USE_LIGHT
}
# endif
}
if ( pwm_present ) {
ResponseAppend_P ( PSTR ( " , " ) ) ;
MqttShowPWMState ( ) ;
}
2020-02-20 09:07:00 +00:00
int32_t rssi = WiFi . RSSI ( ) ;
2019-12-08 18:42:08 +00: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_RSSI " \" :%d, \" " D_JSON_SIGNAL " \" :%d, \" " D_JSON_LINK_COUNT " \" :%d, \" " D_JSON_DOWNTIME " \" : \" %s \" }} " ) ,
2019-12-16 14:13:57 +00:00
Settings . sta_active + 1 , SettingsText ( SET_STASSID1 + Settings . sta_active ) , WiFi . BSSIDstr ( ) . c_str ( ) , WiFi . channel ( ) ,
2020-02-20 09:07:00 +00:00
WifiGetRssiAsQuality ( rssi ) , rssi , WifiLinkCount ( ) , WifiDowntime ( ) . c_str ( ) ) ;
2019-12-02 09:44:27 +00:00
}
void MqttPublishTeleState ( void )
{
mqtt_data [ 0 ] = ' \0 ' ;
MqttShowState ( ) ;
MqttPublishPrefixTopic_P ( TELE , PSTR ( D_RSLT_STATE ) , MQTT_TELE_RETAIN ) ;
2019-12-02 18:04:38 +00:00
# if defined(USE_RULES) || defined(USE_SCRIPT)
2019-12-02 09:44:27 +00:00
RulesTeleperiod ( ) ; // Allow rule based HA messages
# endif // USE_SCRIPT
}
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
}
}
2019-12-02 09:44:27 +00:00
bool MqttShowSensor ( void )
{
ResponseAppendTime ( ) ;
int json_data_start = strlen ( mqtt_data ) ;
for ( uint32_t i = 0 ; i < MAX_SWITCHES ; i + + ) {
# ifdef USE_TM1638
2020-04-27 11:54:07 +01:00
if ( PinUsed ( GPIO_SWT1 , i ) | | ( PinUsed ( GPIO_TM16CLK ) & & PinUsed ( GPIO_TM16DIO ) & & PinUsed ( GPIO_TM16STB ) ) ) {
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-02-06 13:53:35 +00:00
ResponseAppend_P ( PSTR ( " , \" " D_JSON_SWITCH " %d \" : \" %s \" " ) , i + 1 , GetStateText ( SwitchState ( i ) ) ) ;
2019-12-02 09:44:27 +00:00
}
}
XsnsCall ( FUNC_JSON_APPEND ) ;
XdrvCall ( FUNC_JSON_APPEND ) ;
bool json_data_available = ( strlen ( mqtt_data ) - json_data_start ) ;
if ( strstr_P ( mqtt_data , PSTR ( D_JSON_PRESSURE ) ) ! = nullptr ) {
ResponseAppend_P ( PSTR ( " , \" " D_JSON_PRESSURE_UNIT " \" : \" %s \" " ) , PressureUnit ( ) . c_str ( ) ) ;
}
if ( strstr_P ( mqtt_data , PSTR ( D_JSON_TEMPERATURE ) ) ! = nullptr ) {
ResponseAppend_P ( PSTR ( " , \" " D_JSON_TEMPERATURE_UNIT " \" : \" %c \" " ) , TempUnit ( ) ) ;
}
2020-03-02 14:51:33 +00:00
if ( ( strstr_P ( mqtt_data , PSTR ( D_JSON_SPEED ) ) ! = nullptr ) & & Settings . flag2 . speed_conversion ) {
ResponseAppend_P ( PSTR ( " , \" " D_JSON_SPEED_UNIT " \" : \" %s \" " ) , SpeedUnit ( ) . c_str ( ) ) ;
}
2019-12-02 09:44:27 +00:00
ResponseJsonEnd ( ) ;
if ( json_data_available ) { XdrvCall ( FUNC_SHOW_SENSOR ) ; }
return json_data_available ;
}
void MqttPublishSensor ( void )
{
mqtt_data [ 0 ] = ' \0 ' ;
if ( MqttShowSensor ( ) ) {
MqttPublishTeleSensor ( ) ;
}
}
2020-01-18 15:57:48 +00:00
/*********************************************************************************************\
* State loops
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*-------------------------------------------------------------------------------------------*\
* Every second
\ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2019-12-02 09:44:27 +00:00
void PerformEverySecond ( void )
{
uptime + + ;
if ( POWER_CYCLE_TIME = = uptime ) {
UpdateQuickPowerCycle ( false ) ;
}
if ( BOOT_LOOP_TIME = = uptime ) {
RtcRebootReset ( ) ;
# ifdef USE_DEEPSLEEP
if ( ! ( DeepSleepEnabled ( ) & & ! Settings . flag3 . bootcount_update ) ) {
# endif
Settings . bootcount + + ; // Moved to here to stop flash writes during start-up
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_APPLICATION D_BOOT_COUNT " %d " ) , Settings . bootcount ) ;
# ifdef USE_DEEPSLEEP
}
# endif
}
2020-01-18 15:57:48 +00:00
if ( mqtt_cmnd_blocked_reset ) {
mqtt_cmnd_blocked_reset - - ;
if ( ! mqtt_cmnd_blocked_reset ) {
mqtt_cmnd_blocked = 0 ; // Clean up MQTT cmnd loop block
}
}
2019-12-02 09:44:27 +00:00
if ( seriallog_timer ) {
seriallog_timer - - ;
if ( ! seriallog_timer ) {
if ( seriallog_level ) {
AddLog_P ( LOG_LEVEL_INFO , PSTR ( D_LOG_APPLICATION D_SERIAL_LOGGING_DISABLED ) ) ;
}
seriallog_level = 0 ;
}
}
if ( syslog_timer ) { // Restore syslog level
syslog_timer - - ;
if ( ! syslog_timer ) {
syslog_level = Settings . syslog_level ;
if ( Settings . syslog_level ) {
AddLog_P ( LOG_LEVEL_INFO , PSTR ( D_LOG_APPLICATION D_SYSLOG_LOGGING_REENABLED ) ) ; // Might trigger disable again (on purpose)
}
}
}
ResetGlobalValues ( ) ;
if ( Settings . tele_period ) {
if ( tele_period > = 9999 ) {
if ( ! global_state . wifi_down ) {
tele_period = 0 ; // Allow teleperiod once wifi is connected
}
} else {
tele_period + + ;
if ( tele_period > = Settings . tele_period ) {
tele_period = 0 ;
MqttPublishTeleState ( ) ;
mqtt_data [ 0 ] = ' \0 ' ;
if ( MqttShowSensor ( ) ) {
MqttPublishPrefixTopic_P ( TELE , PSTR ( D_RSLT_SENSOR ) , Settings . flag . mqtt_sensor_retain ) ; // CMND_SENSORRETAIN
# if defined(USE_RULES) || defined(USE_SCRIPT)
RulesTeleperiod ( ) ; // Allow rule based HA messages
# endif // USE_RULES
}
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
2020-04-09 13:00:02 +01:00
# ifndef ARDUINO_ESP8266_RELEASE_2_3_0
2020-04-07 13:07:00 +01:00
// Wifi keep alive to send Gratuitous ARP
wifiKeepAlive ( ) ;
2020-04-09 13:00:02 +01:00
# endif // ARDUINO_ESP8266_RELEASE_2_3_0
2020-04-13 16:45:06 +01:00
# ifdef ESP32
if ( 11 = = uptime ) { // Perform one-time ESP32 houskeeping
ESP_getSketchSize ( ) ; // Init sketchsize as it can take up to 2 seconds
}
# 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-01-14 11:47:48 +00:00
if ( prepped_loglevel ) {
AddLog ( prepped_loglevel ) ;
}
2019-12-02 09:44:27 +00:00
if ( latching_relay_pulse ) {
latching_relay_pulse - - ;
if ( ! latching_relay_pulse ) SetLatchingRelay ( 0 , 0 ) ;
}
for ( uint32_t i = 0 ; i < MAX_PULSETIMERS ; i + + ) {
if ( pulse_timer [ i ] ! = 0L ) { // Timer active?
if ( TimeReached ( pulse_timer [ i ] ) ) { // Timer finished?
pulse_timer [ i ] = 0L ; // Turn off this timer
ExecuteCommandPower ( i + 1 , ( POWER_ALL_OFF_PULSETIME_ON = = Settings . poweronstate ) ? POWER_ON : POWER_OFF , SRC_PULSETIMER ) ;
}
}
}
if ( blink_mask ) {
if ( TimeReached ( blink_timer ) ) {
SetNextTimeInterval ( blink_timer , 100 * Settings . blinktime ) ;
blink_counter - - ;
if ( ! blink_counter ) {
StopAllPowerBlink ( ) ;
} else {
blink_power ^ = 1 ;
power_now = ( power & ( POWER_MASK ^ blink_mask ) ) | ( ( blink_power ) ? blink_mask : 0 ) ;
SetDevicePower ( power_now , SRC_IGNORE ) ;
}
}
}
}
/*-------------------------------------------------------------------------------------------*\
* Every 0.25 second
\ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void Every250mSeconds ( void )
{
// As the max amount of sleep = 250 mSec this loop should always be taken...
uint32_t blinkinterval = 1 ;
state_250mS + + ;
state_250mS & = 0x3 ;
if ( ! Settings . flag . global_state ) { // Problem blinkyblinky enabled - SetOption31 - Control link led blinking
if ( global_state . data ) { // Any problem
if ( global_state . mqtt_down ) { blinkinterval = 7 ; } // MQTT problem so blink every 2 seconds (slowest)
if ( global_state . wifi_down ) { blinkinterval = 3 ; } // Wifi problem so blink every second (slow)
blinks = 201 ; // Allow only a single blink in case the problem is solved
}
}
if ( blinks | | restart_flag | | ota_state_flag ) {
if ( restart_flag | | ota_state_flag ) { // Overrule blinks and keep led lit
blinkstate = true ; // Stay lit
} else {
blinkspeed - - ;
if ( ! blinkspeed ) {
blinkspeed = blinkinterval ; // Set interval to 0.2 (default), 1 or 2 seconds
blinkstate ^ = 1 ; // Blink
}
}
if ( ( ! ( Settings . ledstate & 0x08 ) ) & & ( ( Settings . ledstate & 0x06 ) | | ( blinks > 200 ) | | ( blinkstate ) ) ) {
SetLedLink ( blinkstate ) ; // Set led on or off
}
if ( ! blinkstate ) {
blinks - - ;
if ( 200 = = blinks ) blinks = 0 ; // Disable blink
}
}
2020-04-27 11:54:07 +01:00
if ( Settings . ledstate & 1 & & ( PinUsed ( GPIO_LEDLNK ) | | ! ( blinks | | restart_flag | | ota_state_flag ) ) ) {
2019-12-02 09:44:27 +00:00
bool tstate = power & Settings . ledmask ;
2020-04-10 17:24:08 +01:00
# ifdef ESP8266
2019-12-02 09:44:27 +00:00
if ( ( SONOFF_TOUCH = = my_module_type ) | | ( SONOFF_T11 = = my_module_type ) | | ( SONOFF_T12 = = my_module_type ) | | ( SONOFF_T13 = = my_module_type ) ) {
tstate = ( ! power ) ? 1 : 0 ; // As requested invert signal for Touch devices to find them in the dark
}
2020-04-10 17:24:08 +01:00
# endif // ESP8266
2019-12-02 09:44:27 +00:00
SetLedPower ( tstate ) ;
}
/*-------------------------------------------------------------------------------------------*\
* Every second at 0.25 second interval
\ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
switch ( state_250mS ) {
case 0 : // Every x.0 second
if ( ota_state_flag & & BACKLOG_EMPTY ) {
ota_state_flag - - ;
if ( 2 = = ota_state_flag ) {
RtcSettings . ota_loader = 0 ; // Try requested image first
ota_retry_counter = OTA_ATTEMPTS ;
ESPhttpUpdate . rebootOnUpdate ( false ) ;
SettingsSave ( 1 ) ; // Free flash for OTA update
}
if ( ota_state_flag < = 0 ) {
# ifdef USE_WEBSERVER
if ( Settings . webserver ) StopWebserver ( ) ;
# endif // USE_WEBSERVER
# ifdef USE_ARILUX_RF
AriluxRfDisable ( ) ; // Prevent restart exception on Arilux Interrupt routine
# endif // USE_ARILUX_RF
ota_state_flag = 92 ;
ota_result = 0 ;
ota_retry_counter - - ;
if ( ota_retry_counter ) {
strlcpy ( mqtt_data , GetOtaUrl ( log_data , sizeof ( log_data ) ) , sizeof ( mqtt_data ) ) ;
# 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
char * bch = strrchr ( mqtt_data , ' / ' ) ; // Only consider filename after last backslash prevent change of urls having "-" in it
if ( bch = = nullptr ) { bch = mqtt_data ; } // No path found so use filename only
2020-03-04 14:36:37 +00:00
/*
2020-01-17 16:14:53 +00:00
char * ech = strrchr ( bch , ' . ' ) ; // Find file type in filename (none, .bin or .gz)
2020-01-17 14:38:03 +00:00
if ( ( ech ! = nullptr ) & & ( 0 = = strncasecmp_P ( ech , PSTR ( " .GZ " ) , 3 ) ) ) {
char * fch = ech ;
* fch = ' \0 ' ;
2020-01-17 16:14:53 +00:00
ech = strrchr ( bch , ' . ' ) ; // Find file type .bin.gz
2020-01-17 14:38:03 +00:00
* fch = ' . ' ;
}
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)
2020-03-04 17:02:27 +00:00
if ( ech = = nullptr ) { ech = mqtt_data + strlen ( mqtt_data ) ; } // Point to '/0' at end of mqtt_data becoming an empty string
2020-03-04 14:36:37 +00:00
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("OTA: File type [%s]"), ech);
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
* pch = ' \0 ' ; // mqtt_data = http://domus1:80/api/arduino/tasmota
snprintf_P ( mqtt_data , sizeof ( mqtt_data ) , PSTR ( " %s- " D_JSON_MINIMAL " %s " ) , mqtt_data , ota_url_type ) ; // Minimal filename must be filename-minimal
2019-12-02 09:44:27 +00:00
}
# endif // FIRMWARE_MINIMAL
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_UPLOAD " %s " ) , mqtt_data ) ;
# if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2)
ota_result = ( HTTP_UPDATE_FAILED ! = ESPhttpUpdate . update ( mqtt_data ) ) ;
# else
// If using core stage or 2.5.0+ the syntax has changed
WiFiClient OTAclient ;
ota_result = ( HTTP_UPDATE_FAILED ! = ESPhttpUpdate . update ( OTAclient , mqtt_data ) ) ;
# endif
if ( ! ota_result ) {
# ifndef FIRMWARE_MINIMAL
int ota_error = ESPhttpUpdate . getLastError ( ) ;
DEBUG_CORE_LOG ( PSTR ( " OTA: Error %d " ) , ota_error ) ;
if ( ( HTTP_UE_TOO_LESS_SPACE = = ota_error ) | | ( HTTP_UE_BIN_FOR_WRONG_FLASH = = ota_error ) ) {
RtcSettings . ota_loader = 1 ; // Try minimal image next
}
# endif // FIRMWARE_MINIMAL
ota_state_flag = 2 ; // Upgrade failed - retry
}
}
}
if ( 90 = = ota_state_flag ) { // Allow MQTT to reconnect
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 ) {
// SetFlashModeDout(); // Force DOUT for both ESP8266 and ESP8285
2020-01-07 16:01:48 +00:00
if ( ! VersionCompatible ( ) ) {
2019-12-20 14:12:44 +00:00
ResponseAppend_P ( PSTR ( D_JSON_FAILED " " D_UPLOAD_ERR_14 ) ) ;
} else {
ResponseAppend_P ( PSTR ( D_JSON_SUCCESSFUL " . " D_JSON_RESTARTING ) ) ;
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 ( " \" } " ) ) ;
// restart_flag = 2; // Restart anyway to keep memory clean webserver
2019-12-02 09:44:27 +00:00
MqttPublishPrefixTopic_P ( STAT , PSTR ( D_CMND_UPGRADE ) ) ;
}
}
break ;
case 1 : // Every x.25 second
if ( MidnightNow ( ) ) {
XsnsCall ( FUNC_SAVE_AT_MIDNIGHT ) ;
}
if ( save_data_counter & & BACKLOG_EMPTY ) {
save_data_counter - - ;
if ( save_data_counter < = 0 ) {
if ( Settings . flag . save_state ) { // SetOption0 - Save power state and use after restart
power_t mask = POWER_MASK ;
for ( uint32_t i = 0 ; i < MAX_PULSETIMERS ; i + + ) {
if ( ( Settings . pulse_timer [ i ] > 0 ) & & ( Settings . pulse_timer [ i ] < 30 ) ) { // 3 seconds
mask & = ~ ( 1 < < i ) ;
}
}
if ( ! ( ( Settings . power & mask ) = = ( power & mask ) ) ) {
Settings . power = power ;
}
} else {
Settings . power = 0 ;
}
SettingsSave ( 0 ) ;
save_data_counter = Settings . save_data ;
}
}
if ( restart_flag & & BACKLOG_EMPTY ) {
if ( ( 214 = = restart_flag ) | | ( 215 = = restart_flag ) | | ( 216 = = restart_flag ) ) {
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 ) ) ;
uint16_t mqtt_port = Settings . mqtt_port ;
// if (216 == restart_flag) {
// Backup mqtt host, port, client, username and password
// }
2019-12-02 09:44:27 +00:00
if ( ( 215 = = restart_flag ) | | ( 216 = = restart_flag ) ) {
SettingsErase ( 0 ) ; // Erase all flash from program end to end of physical flash
}
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 ) ;
2019-12-02 09:44:27 +00:00
if ( 216 = = restart_flag ) {
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 ) ;
Settings . mqtt_port = mqtt_port ;
2019-12-02 09:44:27 +00:00
}
restart_flag = 2 ;
}
else if ( 213 = = restart_flag ) {
SettingsSdkErase ( ) ; // Erase flash SDK parameters
restart_flag = 2 ;
}
else if ( 212 = = restart_flag ) {
SettingsErase ( 0 ) ; // Erase all flash from program end to end of physical flash
restart_flag = 211 ;
}
if ( 211 = = restart_flag ) {
SettingsDefault ( ) ;
restart_flag = 2 ;
}
if ( 2 = = restart_flag ) {
SettingsSaveAll ( ) ;
}
restart_flag - - ;
if ( restart_flag < = 0 ) {
AddLog_P ( LOG_LEVEL_INFO , PSTR ( D_LOG_APPLICATION D_RESTARTING ) ) ;
EspRestart ( ) ;
}
}
break ;
case 2 : // Every x.5 second
WifiCheck ( wifi_state_flag ) ;
wifi_state_flag = WIFI_RESTART ;
break ;
case 3 : // Every x.75 second
if ( ! global_state . wifi_down ) { MqttCheck ( ) ; }
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 ) ;
ArduinoOTA . setHostname ( my_hostname ) ;
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
if ( Settings . webserver ) { StopWebserver ( ) ; }
# endif // USE_WEBSERVER
# ifdef USE_ARILUX_RF
AriluxRfDisable ( ) ; // Prevent restart exception on Arilux Interrupt routine
# endif // USE_ARILUX_RF
if ( Settings . flag . mqtt_enabled ) {
MqttDisconnect ( ) ; // SetOption3 - Enable MQTT
}
AddLog_P2 ( LOG_LEVEL_INFO , PSTR ( D_LOG_UPLOAD " Arduino OTA " D_UPLOAD_STARTED ) ) ;
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 )
{
if ( ( LOG_LEVEL_DEBUG < = seriallog_level ) ) {
arduino_ota_progress_dot_count + + ;
Serial . printf ( " . " ) ;
if ( ! ( arduino_ota_progress_dot_count % 80 ) ) { Serial . println ( ) ; }
}
} ) ;
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 ] ;
if ( ( LOG_LEVEL_DEBUG < = seriallog_level ) & & arduino_ota_progress_dot_count ) { Serial . println ( ) ; }
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 ) ;
}
AddLog_P2 ( LOG_LEVEL_INFO , PSTR ( D_LOG_UPLOAD " Arduino OTA %s. " D_RESTARTING ) , error_str ) ;
EspRestart ( ) ;
} ) ;
ArduinoOTA . onEnd ( [ ] ( )
{
if ( ( LOG_LEVEL_DEBUG < = seriallog_level ) ) { Serial . println ( ) ; }
AddLog_P2 ( LOG_LEVEL_INFO , PSTR ( D_LOG_UPLOAD " Arduino OTA " D_SUCCESSFUL " . " D_RESTARTING ) ) ;
EspRestart ( ) ;
} ) ;
ArduinoOTA . begin ( ) ;
AddLog_P2 ( LOG_LEVEL_INFO , PSTR ( D_LOG_UPLOAD " Arduino OTA " D_ENABLED " " D_PORT " 8266 " ) ) ;
}
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 )
{
while ( Serial . available ( ) ) {
// yield();
delay ( 0 ) ;
serial_in_byte = Serial . read ( ) ;
2020-05-11 14:27:29 +01:00
if ( 0 = = serial_in_byte_counter ) {
serial_buffer_overrun = false ;
}
else if ( ( serial_in_byte_counter = = INPUT_BUFFER_SIZE )
# 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
\ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
if ( ( SONOFF_DUAL = = my_module_type ) | | ( CH4 = = my_module_type ) ) {
serial_in_byte = ButtonSerial ( serial_in_byte ) ;
}
2020-04-10 17:24:08 +01:00
# endif // ESP8266
2019-12-02 09:44:27 +00:00
/*-------------------------------------------------------------------------------------------*/
if ( XdrvCall ( FUNC_SERIAL ) ) {
serial_in_byte_counter = 0 ;
Serial . flush ( ) ;
return ;
}
/*-------------------------------------------------------------------------------------------*/
if ( serial_in_byte > 127 & & ! Settings . flag . mqtt_serial_raw ) { // Discard binary data above 127 if no raw reception allowed - CMND_SERIALSEND3
serial_in_byte_counter = 0 ;
Serial . flush ( ) ;
return ;
}
if ( ! Settings . flag . mqtt_serial ) { // SerialSend active - CMND_SERIALSEND and CMND_SERIALLOG
if ( isprint ( serial_in_byte ) ) { // Any char between 32 and 127
if ( serial_in_byte_counter < INPUT_BUFFER_SIZE - 1 ) { // Add char to string if it still fits
serial_in_buffer [ serial_in_byte_counter + + ] = serial_in_byte ;
} else {
2020-05-11 14:27:29 +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 {
if ( serial_in_byte | | Settings . flag . mqtt_serial_raw ) { // Any char between 1 and 127 or any char (0 - 255) - CMND_SERIALSEND3
if ( ( serial_in_byte_counter < INPUT_BUFFER_SIZE - 1 ) & & // Add char to string if it still fits and ...
( ( isprint ( serial_in_byte ) & & ( 128 = = Settings . serial_delimiter ) ) | | // Any char between 32 and 127
( ( serial_in_byte ! = Settings . serial_delimiter ) & & ( 128 ! = Settings . serial_delimiter ) ) | | // Any char between 1 and 127 and not being delimiter
Settings . flag . mqtt_serial_raw ) ) { // Any char between 0 and 255 - CMND_SERIALSEND3
serial_in_buffer [ serial_in_byte_counter + + ] = serial_in_byte ;
serial_polling_window = millis ( ) ;
} else {
serial_polling_window = 0 ; // Reception done - send mqtt
break ;
}
}
}
# ifdef USE_SONOFF_SC
/*-------------------------------------------------------------------------------------------*\
* Sonoff SC 19200 baud serial interface
\ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
if ( SONOFF_SC = = my_module_type ) {
if ( serial_in_byte = = ' \x1B ' ) { // Sonoff SC status from ATMEGA328P
serial_in_buffer [ serial_in_byte_counter ] = 0 ; // Serial data completed
SonoffScSerialInput ( serial_in_buffer ) ;
serial_in_byte_counter = 0 ;
Serial . flush ( ) ;
return ;
}
} else
# endif // USE_SONOFF_SC
/*-------------------------------------------------------------------------------------------*/
if ( ! Settings . flag . mqtt_serial & & ( serial_in_byte = = ' \n ' ) ) { // CMND_SERIALSEND and CMND_SERIALLOG
serial_in_buffer [ serial_in_byte_counter ] = 0 ; // Serial data completed
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 ) {
AddLog_P2 ( LOG_LEVEL_INFO , PSTR ( D_LOG_COMMAND " Serial buffer overrun " ) ) ;
} else {
AddLog_P2 ( LOG_LEVEL_INFO , PSTR ( D_LOG_COMMAND " %s " ) , serial_in_buffer ) ;
ExecuteCommand ( serial_in_buffer , SRC_SERIAL ) ;
}
2019-12-02 09:44:27 +00:00
serial_in_byte_counter = 0 ;
serial_polling_window = 0 ;
Serial . flush ( ) ;
return ;
}
}
if ( Settings . flag . mqtt_serial & & serial_in_byte_counter & & ( millis ( ) > ( serial_polling_window + SERIAL_POLLING ) ) ) { // CMND_SERIALSEND and CMND_SERIALLOG
serial_in_buffer [ serial_in_byte_counter ] = 0 ; // Serial data completed
char hex_char [ ( serial_in_byte_counter * 2 ) + 2 ] ;
2020-01-14 14:58:56 +00:00
bool assume_json = ( ! Settings . flag . mqtt_serial_raw & & ( serial_in_buffer [ 0 ] = = ' { ' ) ) ;
Response_P ( PSTR ( " { \" " D_JSON_SERIALRECEIVED " \" :%s%s%s} " ) ,
2020-04-01 13:39:43 +01:00
( assume_json ) ? " " : " \" " ,
2020-01-14 14:58:56 +00:00
( Settings . flag . mqtt_serial_raw ) ? ToHex_P ( ( unsigned char * ) serial_in_buffer , serial_in_byte_counter , hex_char , sizeof ( hex_char ) ) : serial_in_buffer ,
2020-04-01 13:39:43 +01:00
( assume_json ) ? " " : " \" " ) ;
2019-12-02 09:44:27 +00:00
MqttPublishPrefixTopic_P ( RESULT_OR_TELE , PSTR ( D_JSON_SERIALRECEIVED ) ) ;
XdrvRulesProcess ( ) ;
serial_in_byte_counter = 0 ;
}
}
/********************************************************************************************/
2020-03-22 16:42:32 +00:00
void ResetPwm ( void )
{
for ( uint32_t i = 0 ; i < MAX_PWMS ; i + + ) { // Basic PWM control only
2020-04-27 11:54:07 +01:00
if ( PinUsed ( GPIO_PWM1 , i ) ) {
2020-04-26 16:33:27 +01:00
analogWrite ( Pin ( GPIO_PWM1 , i ) , bitRead ( pwm_inverted , i ) ? Settings . pwm_range : 0 ) ;
// analogWrite(Pin(GPIO_PWM1, i), bitRead(pwm_inverted, i) ? Settings.pwm_range - Settings.pwm_value[i] : Settings.pwm_value[i]);
2020-03-22 16:42:32 +00:00
}
}
}
/********************************************************************************************/
2019-12-02 09:44:27 +00:00
void GpioInit ( void )
{
if ( ! ValidModule ( Settings . module ) ) {
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
}
2019-12-02 09:44:27 +00:00
Settings . module = module ;
Settings . last_module = module ;
}
SetModuleType ( ) ;
if ( Settings . module ! = Settings . last_module ) {
2019-12-29 12:27:48 +00:00
Settings . baudrate = APP_BAUDRATE / 300 ;
2019-12-30 13:23:37 +00:00
Settings . serial_config = TS_SERIAL_8N1 ;
2019-12-02 09:44:27 +00:00
}
2020-04-25 10:37:36 +01:00
for ( uint32_t i = 0 ; i < ARRAY_SIZE ( Settings . user_template . gp . io ) ; i + + ) {
2020-04-29 16:44:03 +01:00
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
}
}
myio def_gp ;
ModuleGpios ( & def_gp ) ;
2020-04-25 10:37:36 +01:00
for ( uint32_t i = 0 ; i < ARRAY_SIZE ( Settings . my_gp . io ) ; i + + ) {
2020-04-29 16:44:03 +01:00
if ( ( Settings . my_gp . io [ i ] > = AGPIO ( GPIO_SENSOR_END ) ) & & ( Settings . my_gp . io [ i ] < AGPIO ( GPIO_USER ) ) ) {
2019-12-02 09:44:27 +00:00
Settings . my_gp . io [ i ] = GPIO_NONE ; // Fix not supported sensor ids in module
}
else if ( Settings . my_gp . io [ i ] > GPIO_NONE ) {
my_module . io [ i ] = Settings . my_gp . io [ i ] ; // Set User selected Module sensors
}
2020-04-29 16:44:03 +01:00
if ( ( def_gp . io [ i ] > GPIO_NONE ) & & ( def_gp . io [ i ] < AGPIO ( GPIO_USER ) ) ) {
2019-12-02 09:44:27 +00:00
my_module . io [ i ] = def_gp . io [ i ] ; // Force Template override
}
}
2020-05-01 15:47:41 +01:00
# ifdef ESP8266
2019-12-02 09:44:27 +00:00
if ( ( Settings . my_adc0 > = ADC0_END ) & & ( Settings . my_adc0 < ADC0_USER ) ) {
Settings . my_adc0 = ADC0_NONE ; // Fix not supported sensor ids in module
}
else if ( Settings . my_adc0 > ADC0_NONE ) {
my_adc0 = Settings . my_adc0 ; // Set User selected Module sensors
}
my_module_flag = ModuleFlag ( ) ;
uint32_t template_adc0 = my_module_flag . data & 15 ;
if ( ( template_adc0 > ADC0_NONE ) & & ( template_adc0 < ADC0_USER ) ) {
my_adc0 = template_adc0 ; // Force Template override
}
2020-05-01 15:47:41 +01:00
# endif
2019-12-02 09:44:27 +00:00
2020-04-25 10:37:36 +01:00
for ( uint32_t i = 0 ; i < ARRAY_SIZE ( my_module . io ) ; i + + ) {
2020-04-26 16:33:27 +01:00
uint32_t mpin = ValidPin ( i , 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-04-29 16:44:03 +01:00
if ( ( mpin > = AGPIO ( GPIO_SWT1_NP ) ) & & ( mpin < ( AGPIO ( GPIO_SWT1_NP ) + MAX_SWITCHES ) ) ) {
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-04-29 16:44:03 +01:00
else if ( ( mpin > = AGPIO ( GPIO_REL1_INV ) ) & & ( mpin < ( AGPIO ( GPIO_REL1_INV ) + MAX_RELAYS ) ) ) {
bitSet ( rel_inverted , mpin - AGPIO ( GPIO_REL1_INV ) ) ;
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 ) ) ) {
bitSet ( led_inverted , mpin - AGPIO ( GPIO_LED1_INV ) ) ;
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 ) ) {
2019-12-02 09:44:27 +00:00
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
}
2020-04-29 16:44:03 +01:00
else if ( ( mpin > = AGPIO ( GPIO_PWM1_INV ) ) & & ( mpin < ( AGPIO ( GPIO_PWM1_INV ) + MAX_PWMS ) ) ) {
bitSet ( pwm_inverted , mpin - AGPIO ( GPIO_PWM1_INV ) ) ;
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
}
2020-04-29 16:44:03 +01:00
// AddLogBufferSize(LOG_LEVEL_DEBUG, (uint8_t*)gpio_pin, ARRAY_SIZE(gpio_pin), sizeof(gpio_pin[0]));
2020-04-28 13:42:47 +01:00
2020-04-10 17:24:08 +01:00
# ifdef ESP8266
2020-04-26 16:33:27 +01:00
if ( ( 2 = = Pin ( GPIO_TXD ) ) | | ( H801 = = my_module_type ) ) { Serial . set_tx ( 2 ) ; }
2019-12-02 09:44:27 +00:00
analogWriteRange ( Settings . pwm_range ) ; // Default is 1023 (Arduino.h)
analogWriteFreq ( Settings . pwm_frequency ) ; // Default is 1000 (core_esp8266_wiring_pwm.c)
# ifdef USE_SPI
2020-04-27 11:54:07 +01:00
spi_flg = ( ( ( PinUsed ( GPIO_SPI_CS ) & & ( Pin ( GPIO_SPI_CS ) > 14 ) ) | | ( Pin ( GPIO_SPI_CS ) < 12 ) ) | | ( ( PinUsed ( GPIO_SPI_DC ) & & ( Pin ( GPIO_SPI_DC ) > 14 ) ) | | ( Pin ( GPIO_SPI_DC ) < 12 ) ) ) ;
2019-12-02 09:44:27 +00:00
if ( spi_flg ) {
my_module . io [ 12 ] = GPIO_SPI_MISO ;
2020-04-26 16:33:27 +01:00
SetPin ( 12 , GPIO_SPI_MISO ) ;
2019-12-02 09:44:27 +00:00
my_module . io [ 13 ] = GPIO_SPI_MOSI ;
2020-04-26 16:33:27 +01:00
SetPin ( 13 , GPIO_SPI_MOSI ) ;
2019-12-02 09:44:27 +00:00
my_module . io [ 14 ] = GPIO_SPI_CLK ;
2020-04-26 16:33:27 +01:00
SetPin ( 14 , GPIO_SPI_CLK ) ;
2019-12-02 09:44:27 +00:00
}
2020-04-28 13:42:47 +01:00
soft_spi_flg = ( PinUsed ( GPIO_SSPI_CS ) & & PinUsed ( GPIO_SSPI_SCLK ) & & ( PinUsed ( GPIO_SSPI_MOSI ) | | PinUsed ( GPIO_SSPI_MISO ) ) ) ;
2019-12-02 09:44:27 +00:00
# endif // USE_SPI
2020-05-03 17:37:12 +01:00
# else // ESP32
analogWriteFreqRange ( 0 , Settings . pwm_frequency , Settings . pwm_range ) ;
# ifdef USE_SPI
spi_flg = ( PinUsed ( GPIO_SPI_CLK ) & & ( PinUsed ( GPIO_SPI_MOSI ) | | PinUsed ( GPIO_SPI_MISO ) ) ) ;
soft_spi_flg = ( PinUsed ( GPIO_SSPI_SCLK ) & & ( PinUsed ( GPIO_SSPI_MOSI ) | | PinUsed ( GPIO_SSPI_MISO ) ) ) ;
# endif // USE_SPI
# endif // ESP8266 - ESP32
2019-12-02 09:44:27 +00:00
2020-03-25 10:29:46 +00:00
// Set any non-used GPIO to INPUT - Related to resetPins() in support_legacy_cores.ino
// Doing it here solves relay toggles at restart.
2020-04-25 10:37:36 +01:00
for ( uint32_t i = 0 ; i < ARRAY_SIZE ( my_module . io ) ; i + + ) {
2020-04-26 16:33:27 +01:00
uint32_t mpin = ValidPin ( i , my_module . io [ i ] ) ;
2020-03-25 10:29:46 +00:00
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("INI: gpio pin %d, mpin %d"), i, mpin);
if ( ( ( i < 6 ) | | ( i > 11 ) ) & & ( 0 = = mpin ) ) { // Skip SPI flash interface
if ( ! ( ( 1 = = i ) | | ( 3 = = i ) ) ) { // Skip serial
pinMode ( i , INPUT ) ;
}
}
}
2019-12-02 09:44:27 +00:00
# ifdef USE_I2C
2020-04-27 11:54:07 +01:00
i2c_flg = ( PinUsed ( GPIO_I2C_SCL ) & & PinUsed ( GPIO_I2C_SDA ) ) ;
2019-12-02 09:44:27 +00:00
if ( i2c_flg ) {
2020-04-26 16:33:27 +01:00
Wire . begin ( Pin ( GPIO_I2C_SDA ) , Pin ( GPIO_I2C_SCL ) ) ;
2019-12-02 09:44:27 +00:00
}
# endif // USE_I2C
devices_present = 0 ;
light_type = LT_BASIC ; // Use basic PWM control if SetOption15 = 0
if ( XdrvCall ( FUNC_MODULE_INIT ) ) {
// Serviced
}
2020-04-10 17:24:08 +01:00
# ifdef ESP8266
2019-12-02 09:44:27 +00:00
else if ( YTF_IR_BRIDGE = = my_module_type ) {
ClaimSerial ( ) ; // Stop serial loopback mode
// devices_present = 1;
}
else if ( SONOFF_DUAL = = my_module_type ) {
devices_present = 2 ;
2019-12-29 12:27:48 +00:00
SetSerial ( 19200 , TS_SERIAL_8N1 ) ;
2019-12-02 09:44:27 +00:00
}
else if ( CH4 = = my_module_type ) {
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
else if ( SONOFF_SC = = my_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
2019-12-10 21:00:38 +00:00
for ( uint32_t i = 0 ; i < MAX_PWMS ; i + + ) { // Basic PWM control only
2020-04-27 11:54:07 +01:00
if ( PinUsed ( GPIO_PWM1 , i ) ) {
2020-04-26 16:33:27 +01:00
pinMode ( Pin ( GPIO_PWM1 , i ) , OUTPUT ) ;
2020-05-01 16:38:40 +01:00
# ifdef ESP32
analogAttach ( Pin ( GPIO_PWM1 , i ) , i ) ;
analogWriteFreqRange ( i , Settings . pwm_frequency , Settings . pwm_range ) ;
# endif
2019-12-10 21:00:38 +00:00
if ( light_type ) {
// force PWM GPIOs to low or high mode, see #7165
2020-04-26 16:33:27 +01:00
analogWrite ( Pin ( GPIO_PWM1 , i ) , bitRead ( pwm_inverted , i ) ? Settings . pwm_range : 0 ) ;
2019-12-10 21:00:38 +00:00
} else {
2019-12-02 09:44:27 +00:00
pwm_present = true ;
2020-04-26 16:33:27 +01:00
analogWrite ( Pin ( GPIO_PWM1 , i ) , bitRead ( pwm_inverted , i ) ? Settings . pwm_range - Settings . pwm_value [ i ] : Settings . pwm_value [ i ] ) ;
2019-12-02 09:44:27 +00:00
}
}
}
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-04-26 16:33:27 +01:00
pinMode ( Pin ( GPIO_REL1 , i ) , OUTPUT ) ;
2019-12-02 09:44:27 +00:00
devices_present + + ;
2020-04-10 17:24:08 +01:00
# ifdef ESP8266
2019-12-02 09:44:27 +00:00
if ( EXS_RELAY = = my_module_type ) {
2020-04-26 16:33:27 +01:00
digitalWrite ( Pin ( GPIO_REL1 , i ) , bitRead ( rel_inverted , i ) ? 1 : 0 ) ;
2019-12-02 09:44:27 +00:00
if ( i & 1 ) { devices_present - - ; }
}
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-04-27 16:16:52 +01:00
if ( ( 3 = = i ) & & ( leds_present < 2 ) & & ! PinUsed ( GPIO_ARIRFSEL ) ) {
2020-05-04 10:07:52 +01:00
SetPin ( Pin ( GPIO_LED1 , i ) , GPIO_ARIRFSEL ) ; // Legacy support where LED4 was Arilux RF enable
2019-12-02 09:44:27 +00:00
} else {
# endif
2020-04-26 16:33:27 +01:00
pinMode ( Pin ( GPIO_LED1 , i ) , OUTPUT ) ;
2019-12-02 09:44:27 +00:00
leds_present + + ;
2020-04-26 16:33:27 +01:00
digitalWrite ( Pin ( GPIO_LED1 , i ) , bitRead ( led_inverted , i ) ) ;
2019-12-02 09:44:27 +00:00
# ifdef USE_ARILUX_RF
}
# endif
}
}
2020-04-27 11:54:07 +01:00
if ( PinUsed ( GPIO_LEDLNK ) ) {
2020-04-26 16:33:27 +01:00
pinMode ( Pin ( GPIO_LEDLNK ) , OUTPUT ) ;
digitalWrite ( Pin ( GPIO_LEDLNK ) , ledlnk_inverted ) ;
2019-12-02 09:44:27 +00:00
}
2020-03-13 17:08:44 +00:00
# ifdef USE_PWM_DIMMER
2020-04-27 11:54:07 +01:00
if ( PWM_DIMMER = = my_module_type & & PinUsed ( GPIO_REL1 ) ) { devices_present - - ; }
2020-03-13 17:08:44 +00:00
# endif // USE_PWM_DIMMER
2019-12-02 09:44:27 +00:00
ButtonInit ( ) ;
SwitchInit ( ) ;
# ifdef ROTARY_V1
RotaryInit ( ) ;
# endif
SetLedPower ( Settings . ledstate & 8 ) ;
SetLedLink ( Settings . ledstate & 8 ) ;
XdrvCall ( FUNC_PRE_INIT ) ;
}