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 ;
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 {
2020-05-26 16:08:13 +01:00
String mac_address = WiFi . macAddress ( ) ;
mac_address . replace ( " : " , " " ) ;
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 ;
}
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 ) ;
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 {
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 ) ;
2020-10-30 11:29:48 +00:00
fulltopic . replace ( F ( " %hostname% " ) , TasmotaGlobal . hostname ) ;
2019-12-02 09:44:27 +00:00
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 )
{
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
if ( POWER_ALL_ALWAYS_ON = = Settings . poweronstate ) { // All on and stay on
2020-10-30 11:29:48 +00:00
TasmotaGlobal . power = ( 1 < < TasmotaGlobal . devices_present ) - 1 ;
2020-10-28 18:03:39 +00:00
rpower = TasmotaGlobal . power ;
2019-12-02 09:44:27 +00:00
}
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 ;
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
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
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
{
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 ;
}
}
}
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
// 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 ) ) {
2020-10-30 11:29:48 +00:00
power_t all_on = ( 1 < < TasmotaGlobal . devices_present ) - 1 ;
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 ) {
2020-01-11 14:39:56 +00:00
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 :
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
2020-10-30 11:29:48 +00:00
TasmotaGlobal . power = ( 1 < < TasmotaGlobal . devices_present ) - 1 ;
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 :
2020-10-30 11:29:48 +00:00
TasmotaGlobal . power = ( Settings . power & ( ( 1 < < TasmotaGlobal . devices_present ) - 1 ) ) ^ POWER_MASK ;
2020-01-11 14:39:56 +00: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 :
2020-10-30 11:29:48 +00:00
TasmotaGlobal . power = Settings . power & ( ( 1 < < TasmotaGlobal . devices_present ) - 1 ) ;
2020-01-11 14:39:56 +00: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 {
2020-10-30 11:29:48 +00:00
TasmotaGlobal . power = Settings . power & ( ( 1 < < TasmotaGlobal . devices_present ) - 1 ) ;
2020-01-11 14:39:56 +00: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 + + ) {
2020-01-11 14:39:56 +00: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
}
}
2020-10-28 18:03:39 +00:00
if ( bitRead ( TasmotaGlobal . power , i ) | | ( POWER_ALL_OFF_PULSETIME_ON = = Settings . poweronstate ) ) {
2020-10-02 16:32:45 +01:00
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 ;
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 )
{
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 ;
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 ;
if ( bitRead ( Settings . ledpwm_mask , led ) ) {
# ifdef USE_LIGHT
pwm = changeUIntScale ( ledGamma10 ( state ? Settings . ledpwm_on : Settings . ledpwm_off ) , 0 , 1023 , 0 , Settings . pwm_range ) ; // gamma corrected
# else //USE_LIGHT
pwm = changeUIntScale ( ( uint16_t ) ( state ? Settings . ledpwm_on : Settings . ledpwm_off ) , 0 , 255 , 0 , Settings . pwm_range ) ; // linear
# endif //USE_LIGHT
2020-10-30 11:29:48 +00:00
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
}
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 ;
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 )
{
2020-04-26 16:33:27 +01:00
uint32_t led_pin = Pin ( GPIO_LEDLNK ) ;
2020-10-30 11:29:48 +00:00
uint32_t led_inv = TasmotaGlobal . ledlnk_inverted ;
2019-12-02 09:44:27 +00:00
if ( 99 = = led_pin ) { // Legacy - LED1 is status
2020-05-23 12:09:16 +01:00
SetLedPowerIdx ( 0 , state ) ;
}
else if ( led_pin < 99 ) {
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
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
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
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 ,
GetPowerDevice ( scommand , device , sizeof ( scommand ) , ( key + Settings . flag . device_index_enable ) ) ) ; // cmnd/switchtopic/POWERx - SetOption26 - Switch between POWER or POWER1
if ( CLEAR_RETAIN = = state ) {
2020-10-30 11:29:48 +00:00
ResponseClear ( ) ;
2019-12-02 09:44:27 +00:00
} else {
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
}
2020-10-30 11:29:48 +00:00
snprintf_P ( TasmotaGlobal . mqtt_data , sizeof ( TasmotaGlobal . mqtt_data ) , GetStateText ( state ) ) ;
2019-12-02 09:44:27 +00:00
}
# ifdef USE_DOMOTICZ
2020-10-30 11:29:48 +00:00
if ( ! ( DomoticzSendKey ( key , device , state , strlen ( TasmotaGlobal . mqtt_data ) ) ) ) {
2019-12-02 09:44:27 +00:00
# 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 ( ) ;
}
2020-10-20 16:48:49 +01:00
# ifdef USE_PWM_DIMMER
2020-11-01 16:52:10 +00:00
if ( PWM_DIMMER ! = TasmotaGlobal . module_type | | ! result ) {
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
// 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 ( ) ) {
2020-10-29 11:58:22 +00:00
TasmotaGlobal . blink_mask & = 1 ; // No blinking on the fan relays
2019-12-02 09:44:27 +00: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 ;
}
# 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
2020-10-02 16:32:45 +01:00
SetPulseTimer ( ( device - 1 ) % MAX_PULSETIMERS , 0 ) ;
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 ) ;
}
if ( Settings . flag . interlock & & // CMND_INTERLOCK - Enable/disable interlock
! 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
for ( uint32_t i = 0 ; i < MAX_INTERLOCKS ; i + + ) {
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 ;
2020-10-28 18:03:39 +00: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 ) ;
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 : {
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
2020-05-27 04:07:25 +01:00
if ( SRC_REMOTE ! = source & & SRC_RETRY ! = source ) {
2020-08-09 16:16:15 +01:00
if ( Settings . flag4 . multiple_device_groups ) // SetOption88 - Enable relays in separate device groups
2020-10-28 18:03:39 +00:00
SendDeviceGroupMessage ( device - 1 , DGR_MSGTYP_UPDATE , DGR_ITEM_POWER , ( TasmotaGlobal . power > > ( device - 1 ) ) & 1 | 0x01000000 ) ; // Explicitly set number of relays to one
2020-05-27 04:07:25 +01:00
else
2020-10-28 18:03:39 +00:00
SendLocalDeviceGroupMessage ( DGR_MSGTYP_UPDATE , DGR_ITEM_POWER , TasmotaGlobal . 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
if ( publish_power & & Settings . flag3 . hass_tele_on_power ) { // SetOption59 - Send tele/%topic%/STATE in addition to stat/%topic%/RESULT
MqttPublishTeleState ( ) ;
}
2020-10-02 16:32:45 +01:00
// Restart PulseTime if powered On
2020-10-28 18:03:39 +00: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 ;
2020-10-29 12:37:09 +00: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 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 ( ) ) ;
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
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-10-29 15:16:34 +00:00
TasmotaGlobal . sleep , TasmotaGlobal . loop_load_avg , MqttConnectCount ( ) ) ;
2019-12-02 09:44:27 +00:00
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
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
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 ( ) ;
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 \" } " ) ,
2020-06-19 11:33:31 +01:00
Settings . sta_active + 1 , EscapeJSONString ( SettingsText ( SET_STASSID1 + Settings . sta_active ) ) . c_str ( ) , WiFi . BSSIDstr ( ) . c_str ( ) , WiFi . channel ( ) ,
2020-06-15 17:27:04 +01:00
WifiGetRssiAsQuality ( rssi ) , rssi , WifiLinkCount ( ) , WifiDowntime ( ) . c_str ( ) ) ;
}
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 ( ) ;
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
}
}
2020-11-01 12:26:35 +00:00
String GetSwitchText ( uint32_t i ) {
String switch_text = SettingsText ( SET_SWITCH_TXT1 + i ) ;
if ( ' \0 ' = = switch_text [ 0 ] ) {
switch_text = D_JSON_SWITCH + String ( i + 1 ) ;
}
return switch_text ;
}
2019-12-02 09:44:27 +00:00
bool MqttShowSensor ( void )
{
ResponseAppendTime ( ) ;
2020-10-30 11:29:48 +00:00
int json_data_start = strlen ( TasmotaGlobal . mqtt_data ) ;
2019-12-02 09:44:27 +00:00
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-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 ) ;
2020-10-30 11:29:48 +00:00
bool json_data_available = ( strlen ( TasmotaGlobal . mqtt_data ) - json_data_start ) ;
if ( strstr_P ( TasmotaGlobal . mqtt_data , PSTR ( D_JSON_PRESSURE ) ) ! = nullptr ) {
2019-12-02 09:44:27 +00:00
ResponseAppend_P ( PSTR ( " , \" " D_JSON_PRESSURE_UNIT " \" : \" %s \" " ) , PressureUnit ( ) . c_str ( ) ) ;
}
2020-10-30 11:29:48 +00:00
if ( strstr_P ( TasmotaGlobal . mqtt_data , PSTR ( D_JSON_TEMPERATURE ) ) ! = nullptr ) {
2019-12-02 09:44:27 +00:00
ResponseAppend_P ( PSTR ( " , \" " D_JSON_TEMPERATURE_UNIT " \" : \" %c \" " ) , TempUnit ( ) ) ;
}
2020-10-30 11:29:48 +00:00
if ( ( strstr_P ( TasmotaGlobal . mqtt_data , PSTR ( D_JSON_SPEED ) ) ! = nullptr ) & & Settings . flag2 . speed_conversion ) {
2020-03-02 14:51:33 +00:00
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 )
{
2020-10-30 11:29:48 +00:00
ResponseClear ( ) ;
2019-12-02 09:44:27 +00:00
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 )
{
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 ( ) ;
2020-11-01 11:12:27 +00:00
Settings . last_module = Settings . module ;
2019-12-02 09:44:27 +00:00
# ifdef USE_DEEPSLEEP
if ( ! ( DeepSleepEnabled ( ) & & ! Settings . flag3 . bootcount_update ) ) {
# endif
Settings . bootcount + + ; // Moved to here to stop flash writes during start-up
2020-11-06 16:09:13 +00:00
AddLog_P ( 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 ) {
2019-12-02 09:44:27 +00:00
AddLog_P ( LOG_LEVEL_INFO , PSTR ( D_LOG_APPLICATION D_SERIAL_LOGGING_DISABLED ) ) ;
}
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 ) {
2020-10-30 11:29:48 +00:00
TasmotaGlobal . syslog_level = Settings . syslog_level ;
2019-12-02 09:44:27 +00:00
if ( Settings . syslog_level ) {
AddLog_P ( LOG_LEVEL_INFO , PSTR ( D_LOG_APPLICATION D_SYSLOG_LOGGING_REENABLED ) ) ; // Might trigger disable again (on purpose)
}
}
}
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 ( ) ;
if ( Settings . 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 + + ;
if ( TasmotaGlobal . tele_period > = Settings . tele_period ) {
TasmotaGlobal . tele_period = 0 ;
2019-12-02 09:44:27 +00:00
MqttPublishTeleState ( ) ;
2020-10-30 11:29:48 +00:00
ResponseClear ( ) ;
2019-12-02 09:44:27 +00:00
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
// Wifi keep alive to send Gratuitous ARP
wifiKeepAlive ( ) ;
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
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
}
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
2020-10-30 11:29:48 +00:00
for ( uint32_t j = 0 ; j < TasmotaGlobal . devices_present ; j = j + MAX_PULSETIMERS ) {
2020-10-02 16:32:45 +01:00
ExecuteCommandPower ( i + j + 1 , ( POWER_ALL_OFF_PULSETIME_ON = = Settings . poweronstate ) ? POWER_ON : POWER_OFF , SRC_PULSETIMER ) ;
}
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 ) ) {
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
\ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
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
2020-10-04 16:08:11 +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
}
}
2020-10-30 11:29:48 +00:00
if ( ( ! ( Settings . ledstate & 0x08 ) ) & & ( ( Settings . ledstate & 0x06 ) | | ( TasmotaGlobal . blinks > 200 ) | | ( TasmotaGlobal . blinkstate ) ) ) {
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
}
}
2020-10-29 11:39:44 +00:00
if ( Settings . ledstate & 1 & & ( PinUsed ( GPIO_LEDLNK ) | | ! ( TasmotaGlobal . blinks | | TasmotaGlobal . restart_flag | | TasmotaGlobal . ota_state_flag ) ) ) {
2020-10-28 18:03:39 +00:00
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
2020-10-29 11:21:24 +00:00
if ( TasmotaGlobal . ota_state_flag & & BACKLOG_EMPTY ) {
TasmotaGlobal . ota_state_flag - - ;
if ( 2 = = TasmotaGlobal . ota_state_flag ) {
2019-12-02 09:44:27 +00:00
RtcSettings . ota_loader = 0 ; // Try requested image first
ota_retry_counter = OTA_ATTEMPTS ;
ESPhttpUpdate . rebootOnUpdate ( false ) ;
SettingsSave ( 1 ) ; // Free flash for OTA update
}
2020-10-29 11:21:24 +00:00
if ( TasmotaGlobal . ota_state_flag < = 0 ) {
2020-07-20 10:20:58 +01:00
# ifdef USE_COUNTER
CounterInterruptDisable ( true ) ; // Prevent OTA failures on 100Hz counter interrupts
# endif // USE_COUNTER
2019-12-02 09:44:27 +00:00
# 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
2020-10-29 11:21:24 +00:00
TasmotaGlobal . ota_state_flag = 92 ;
2019-12-02 09:44:27 +00:00
ota_result = 0 ;
ota_retry_counter - - ;
if ( ota_retry_counter ) {
2020-12-23 17:00:59 +00:00
char ota_url [ TOPSZ ] ;
strlcpy ( TasmotaGlobal . mqtt_data , GetOtaUrl ( ota_url , sizeof ( ota_url ) ) , sizeof ( TasmotaGlobal . mqtt_data ) ) ;
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
2020-10-30 11:29:48 +00:00
char * bch = strrchr ( TasmotaGlobal . mqtt_data , ' / ' ) ; // Only consider filename after last backslash prevent change of urls having "-" in it
if ( bch = = nullptr ) { bch = TasmotaGlobal . mqtt_data ; } // 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)
2020-10-30 11:29:48 +00:00
if ( ech = = nullptr ) { ech = TasmotaGlobal . mqtt_data + strlen ( TasmotaGlobal . mqtt_data ) ; } // Point to '/0' at end of mqtt_data becoming an empty string
2020-03-04 14:36:37 +00:00
2020-11-06 16:09:13 +00:00
//AddLog_P(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
* pch = ' \0 ' ; // mqtt_data = http://domus1:80/api/arduino/tasmota
2020-10-30 11:29:48 +00:00
snprintf_P ( TasmotaGlobal . mqtt_data , sizeof ( TasmotaGlobal . mqtt_data ) , PSTR ( " %s- " D_JSON_MINIMAL " %s " ) , TasmotaGlobal . mqtt_data , ota_url_type ) ; // Minimal filename must be filename-minimal
2019-12-02 09:44:27 +00:00
}
# endif // FIRMWARE_MINIMAL
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_UPLOAD " %s " ) , TasmotaGlobal . mqtt_data ) ;
2019-12-02 09:44:27 +00:00
WiFiClient OTAclient ;
2020-10-30 11:29:48 +00:00
ota_result = ( HTTP_UPDATE_FAILED ! = ESPhttpUpdate . update ( OTAclient , TasmotaGlobal . mqtt_data ) ) ;
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 ) ;
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
2020-10-29 11:21:24 +00:00
TasmotaGlobal . ota_state_flag = 2 ; // Upgrade failed - retry
2019-12-02 09:44:27 +00:00
}
}
}
2020-10-29 11:21:24 +00:00
if ( 90 = = TasmotaGlobal . ota_state_flag ) { // Allow MQTT to reconnect
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 ) {
// SetFlashModeDout(); // Force DOUT for both ESP8266 and ESP8285
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 ( " \" } " ) ) ;
2020-10-29 11:21:24 +00:00
// TasmotaGlobal.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 ) ) ;
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 ) ;
}
2020-10-29 12:58:50 +00:00
if ( TasmotaGlobal . save_data_counter & & BACKLOG_EMPTY ) {
TasmotaGlobal . save_data_counter - - ;
if ( TasmotaGlobal . save_data_counter < = 0 ) {
2019-12-02 09:44:27 +00:00
if ( Settings . flag . save_state ) { // SetOption0 - Save power state and use after restart
power_t mask = POWER_MASK ;
2020-10-30 11:29:48 +00:00
for ( uint32_t i = 0 ; i < TasmotaGlobal . devices_present ; i + + ) {
2020-10-02 16:32:45 +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 ) ;
}
}
2020-10-28 18:03:39 +00:00
if ( ! ( ( Settings . power & mask ) = = ( TasmotaGlobal . power & mask ) ) ) {
Settings . power = TasmotaGlobal . power ;
2019-12-02 09:44:27 +00:00
}
} else {
Settings . power = 0 ;
}
2020-10-29 11:21:24 +00:00
if ( ! TasmotaGlobal . restart_flag ) { SettingsSave ( 0 ) ; }
2020-10-29 12:58:50 +00:00
TasmotaGlobal . save_data_counter = Settings . save_data ;
2019-12-02 09:44:27 +00:00
}
}
2020-10-29 11:21:24 +00:00
if ( TasmotaGlobal . restart_flag & & BACKLOG_EMPTY ) {
if ( ( 214 = = TasmotaGlobal . restart_flag ) | | ( 215 = = TasmotaGlobal . restart_flag ) | | ( 216 = = TasmotaGlobal . 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 ;
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
// }
2020-10-29 11:21:24 +00:00
if ( ( 215 = = TasmotaGlobal . restart_flag ) | | ( 216 = = TasmotaGlobal . restart_flag ) ) {
2019-12-02 09:44:27 +00:00
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 ) ;
2020-10-29 11:21:24 +00:00
if ( 216 = = TasmotaGlobal . 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
}
2020-10-29 11:21:24 +00:00
TasmotaGlobal . restart_flag = 2 ;
2019-12-02 09:44:27 +00:00
}
2020-10-29 11:21:24 +00:00
else if ( 213 = = TasmotaGlobal . restart_flag ) {
2019-12-02 09:44:27 +00:00
SettingsSdkErase ( ) ; // Erase flash SDK parameters
2020-10-29 11:21:24 +00:00
TasmotaGlobal . restart_flag = 2 ;
2019-12-02 09:44:27 +00:00
}
2020-10-29 11:21:24 +00:00
else if ( 212 = = TasmotaGlobal . restart_flag ) {
2019-12-02 09:44:27 +00:00
SettingsErase ( 0 ) ; // Erase all flash from program end to end of physical flash
2020-10-29 11:21:24 +00:00
TasmotaGlobal . restart_flag = 211 ;
2019-12-02 09:44:27 +00:00
}
2020-10-29 11:21:24 +00:00
if ( 211 = = TasmotaGlobal . restart_flag ) {
2019-12-02 09:44:27 +00:00
SettingsDefault ( ) ;
2020-10-29 11:21:24 +00:00
TasmotaGlobal . restart_flag = 2 ;
2019-12-02 09:44:27 +00:00
}
2020-10-29 11:21:24 +00:00
if ( 2 = = TasmotaGlobal . restart_flag ) {
2019-12-02 09:44:27 +00:00
SettingsSaveAll ( ) ;
}
2020-10-29 11:21:24 +00:00
TasmotaGlobal . restart_flag - - ;
if ( TasmotaGlobal . restart_flag < = 0 ) {
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_INFO , PSTR ( D_LOG_APPLICATION " %s " ) , ( TasmotaGlobal . restart_halt ) ? " Halted " : D_RESTARTING ) ;
2019-12-02 09:44:27 +00:00
EspRestart ( ) ;
}
}
break ;
case 2 : // Every x.5 second
2020-06-15 17:27:04 +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 ;
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
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
if ( Settings . webserver ) {
# ifdef ESP8266
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
StartWebserver ( Settings . webserver , ( EthernetLocalIP ( ) ) ? EthernetLocalIP ( ) : WiFi . localIP ( ) ) ;
# else
StartWebserver ( Settings . webserver , WiFi . localIP ( ) ) ;
# 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
if ( Settings . flag2 . emulation ) { UdpConnect ( ) ; }
# endif // USE_EMULATION
# endif // USE_WEBSERVER
# ifdef USE_DEVICE_GROUPS
DeviceGroupsStart ( ) ;
# endif // USE_DEVICE_GROUPS
# ifdef USE_KNX
if ( ! knx_started & & Settings . flag . knx_enabled ) { // CMND_KNX_ENABLED
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
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
}
2020-11-06 16:09:13 +00:00
AddLog_P ( 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 + + ;
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 ] ;
2020-10-30 11:29:48 +00:00
if ( ( LOG_LEVEL_DEBUG < = TasmotaGlobal . seriallog_level ) & & arduino_ota_progress_dot_count ) { Serial . 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 ) ;
}
2020-11-06 16:09:13 +00:00
AddLog_P ( 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 ( [ ] ( )
{
2020-10-30 11:29:48 +00:00
if ( ( LOG_LEVEL_DEBUG < = TasmotaGlobal . seriallog_level ) ) { Serial . println ( ) ; }
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_INFO , PSTR ( D_LOG_UPLOAD " Arduino OTA " D_SUCCESSFUL " . " D_RESTARTING ) ) ;
2019-12-02 09:44:27 +00:00
EspRestart ( ) ;
} ) ;
ArduinoOTA . begin ( ) ;
2020-11-06 16:09:13 +00:00
AddLog_P ( 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 ;
}
/*-------------------------------------------------------------------------------------------*/
2020-10-30 11:29:48 +00: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 ;
}
if ( ! Settings . flag . mqtt_serial ) { // SerialSend active - CMND_SERIALSEND and CMND_SERIALLOG
2020-10-30 11:29:48 +00:00
if ( isprint ( TasmotaGlobal . serial_in_byte ) ) { // Any char between 32 and 127
2020-10-29 11:21:24 +00:00
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 {
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 {
2020-10-30 11:29:48 +00:00
if ( TasmotaGlobal . serial_in_byte | | Settings . flag . mqtt_serial_raw ) { // Any char between 1 and 127 or any char (0 - 255) - CMND_SERIALSEND3
2020-05-23 16:07:52 +01:00
bool in_byte_is_delimiter = // Char is delimiter when...
2020-10-30 11:29:48 +00: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
2020-05-23 16:07:52 +01:00
! Settings . flag . mqtt_serial_raw ; // In raw mode (CMND_SERIALSEND3) there is never a delimiter
2020-10-29 11:21:24 +00:00
if ( ( TasmotaGlobal . serial_in_byte_counter < INPUT_BUFFER_SIZE - 1 ) & & // Add char to string if it still fits and ...
2020-05-23 16:07:52 +01:00
! 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
2020-10-29 11:21:24 +00:00
if ( ( TasmotaGlobal . serial_in_byte_counter > = INPUT_BUFFER_SIZE - 1 ) | | // Send message when buffer is full or ...
2020-05-23 16:30:48 +01:00
in_byte_is_delimiter ) { // Char is delimiter
2019-12-02 09:44:27 +00:00
serial_polling_window = 0 ; // Reception done - send mqtt
break ;
}
2020-05-23 16:30:48 +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 ) {
if ( TasmotaGlobal . serial_in_byte = = ' \x1B ' ) { // Sonoff SC status from ATMEGA328P
TasmotaGlobal . serial_in_buffer [ TasmotaGlobal . serial_in_byte_counter ] = 0 ; // Serial data completed
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
/*-------------------------------------------------------------------------------------------*/
2020-10-30 11:29:48 +00: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
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 ) {
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_INFO , PSTR ( D_LOG_COMMAND " Serial buffer overrun " ) ) ;
2020-05-11 14:27:29 +01:00
} else {
2020-11-06 16:09:13 +00:00
AddLog_P ( 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 ;
}
}
2020-10-29 11:21:24 +00:00
if ( Settings . flag . mqtt_serial & & TasmotaGlobal . serial_in_byte_counter & & ( millis ( ) > ( serial_polling_window + SERIAL_POLLING ) ) ) { // CMND_SERIALSEND and CMND_SERIALLOG
2020-10-30 11:29:48 +00:00
TasmotaGlobal . serial_in_buffer [ TasmotaGlobal . serial_in_byte_counter ] = 0 ; // Serial data completed
bool assume_json = ( ! Settings . flag . mqtt_serial_raw & & ( TasmotaGlobal . serial_in_buffer [ 0 ] = = ' { ' ) ) ;
2020-05-26 11:35:21 +01:00
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 ( " \" " ) ) ;
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
}
}
/********************************************************************************************/
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-10-30 11:29:48 +00:00
analogWrite ( Pin ( GPIO_PWM1 , i ) , bitRead ( TasmotaGlobal . pwm_inverted , i ) ? Settings . pwm_range : 0 ) ;
// analogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.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 ( ) ;
2020-11-06 16:09:13 +00:00
// AddLog_P(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
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
}
}
2020-11-24 14:14:22 +00:00
myio template_gp ;
TemplateGpios ( & template_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 ) {
2020-10-30 11:29:48 +00:00
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
}
}
2020-10-30 11:29:48 +00:00
for ( uint32_t i = 0 ; i < ARRAY_SIZE ( TasmotaGlobal . my_module . io ) ; i + + ) {
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 ;
}
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
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
}
2020-04-29 16:44:03 +01:00
else if ( ( mpin > = AGPIO ( GPIO_PWM1_INV ) ) & & ( mpin < ( AGPIO ( GPIO_PWM1_INV ) + MAX_PWMS ) ) ) {
2020-10-30 11:29:48 +00:00
bitSet ( TasmotaGlobal . pwm_inverted , mpin - AGPIO ( GPIO_PWM1_INV ) ) ;
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
}
2020-10-29 12:58:50 +00:00
// AddLogBufferSize(LOG_LEVEL_DEBUG, (uint8_t*)TasmotaGlobal.gpio_pin, ARRAY_SIZE(TasmotaGlobal.gpio_pin), sizeof(TasmotaGlobal.gpio_pin[0]));
2020-04-28 13:42:47 +01:00
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)
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 ) ; }
2020-08-03 10:52:25 +01:00
2019-12-02 09:44:27 +00:00
# ifdef USE_SPI
2020-10-30 11:29:48 +00:00
TasmotaGlobal . spi_enabled = ( ( ( 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 ) ) ) ;
if ( TasmotaGlobal . spi_enabled ) {
2020-11-30 10:51:27 +00:00
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 ) ) ;
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( " SPI: Using GPIO12(MISO), GPIO13(MOSI) and GPIO14(CLK) " ) ) ;
2019-12-02 09:44:27 +00:00
}
# endif // USE_SPI
2020-11-28 11:46:17 +00:00
# endif // ESP8266
# ifdef ESP32
2020-05-03 17:37:12 +01:00
# ifdef USE_SPI
2020-06-20 16:58:21 +01:00
if ( PinUsed ( GPIO_SPI_CS ) | | PinUsed ( GPIO_SPI_DC ) ) {
if ( ( 15 = = Pin ( GPIO_SPI_CS ) ) & & ( ! GetPin ( 12 ) & & ! GetPin ( 13 ) & & ! GetPin ( 14 ) ) ) { // HSPI
2020-10-30 11:29:48 +00:00
TasmotaGlobal . my_module . io [ 12 ] = AGPIO ( GPIO_SPI_MISO ) ;
2020-06-20 16:58:21 +01:00
SetPin ( 12 , AGPIO ( GPIO_SPI_MISO ) ) ;
2020-10-30 11:29:48 +00:00
TasmotaGlobal . my_module . io [ 13 ] = AGPIO ( GPIO_SPI_MOSI ) ;
2020-06-20 16:58:21 +01:00
SetPin ( 13 , AGPIO ( GPIO_SPI_MOSI ) ) ;
2020-10-30 11:29:48 +00:00
TasmotaGlobal . my_module . io [ 14 ] = AGPIO ( GPIO_SPI_CLK ) ;
2020-06-20 16:58:21 +01:00
SetPin ( 14 , AGPIO ( GPIO_SPI_CLK ) ) ;
}
else if ( ( 5 = = Pin ( GPIO_SPI_CS ) ) & & ( ! GetPin ( 19 ) & & ! GetPin ( 23 ) & & ! GetPin ( 18 ) ) ) { // VSPI
2020-10-30 11:29:48 +00:00
TasmotaGlobal . my_module . io [ 19 ] = AGPIO ( GPIO_SPI_MISO ) ;
2020-06-20 16:58:21 +01:00
SetPin ( 19 , AGPIO ( GPIO_SPI_MISO ) ) ;
2020-10-30 11:29:48 +00:00
TasmotaGlobal . my_module . io [ 23 ] = AGPIO ( GPIO_SPI_MOSI ) ;
2020-06-20 16:58:21 +01:00
SetPin ( 23 , AGPIO ( GPIO_SPI_MOSI ) ) ;
2020-10-30 11:29:48 +00:00
TasmotaGlobal . my_module . io [ 18 ] = AGPIO ( GPIO_SPI_CLK ) ;
2020-06-20 16:58:21 +01:00
SetPin ( 18 , AGPIO ( GPIO_SPI_CLK ) ) ;
}
else if ( ( 12 = = Pin ( GPIO_SPI_MISO ) ) | | ( 13 = = Pin ( GPIO_SPI_MOSI ) ) | | ( 14 = = Pin ( GPIO_SPI_CLK ) ) ) { // HSPI
2020-10-30 11:29:48 +00:00
TasmotaGlobal . my_module . io [ 12 ] = AGPIO ( GPIO_SPI_MISO ) ;
2020-06-20 16:58:21 +01:00
SetPin ( 12 , AGPIO ( GPIO_SPI_MISO ) ) ;
2020-10-30 11:29:48 +00:00
TasmotaGlobal . my_module . io [ 13 ] = AGPIO ( GPIO_SPI_MOSI ) ;
2020-06-20 16:58:21 +01:00
SetPin ( 13 , AGPIO ( GPIO_SPI_MOSI ) ) ;
2020-10-30 11:29:48 +00:00
TasmotaGlobal . my_module . io [ 14 ] = AGPIO ( GPIO_SPI_CLK ) ;
2020-06-20 16:58:21 +01:00
SetPin ( 14 , AGPIO ( GPIO_SPI_CLK ) ) ;
}
else if ( ( 19 = = Pin ( GPIO_SPI_MISO ) ) | | ( 23 = = Pin ( GPIO_SPI_MOSI ) ) | | ( 18 = = Pin ( GPIO_SPI_CLK ) ) ) { // VSPI
2020-10-30 11:29:48 +00:00
TasmotaGlobal . my_module . io [ 19 ] = AGPIO ( GPIO_SPI_MISO ) ;
2020-06-20 16:58:21 +01:00
SetPin ( 19 , AGPIO ( GPIO_SPI_MISO ) ) ;
2020-10-30 11:29:48 +00:00
TasmotaGlobal . my_module . io [ 23 ] = AGPIO ( GPIO_SPI_MOSI ) ;
2020-06-20 16:58:21 +01:00
SetPin ( 23 , AGPIO ( GPIO_SPI_MOSI ) ) ;
2020-10-30 11:29:48 +00:00
TasmotaGlobal . my_module . io [ 18 ] = AGPIO ( GPIO_SPI_CLK ) ;
2020-06-20 16:58:21 +01:00
SetPin ( 18 , AGPIO ( GPIO_SPI_CLK ) ) ;
}
2020-10-30 11:29:48 +00:00
TasmotaGlobal . spi_enabled = ( PinUsed ( GPIO_SPI_CLK ) & & ( PinUsed ( GPIO_SPI_MOSI ) | | PinUsed ( GPIO_SPI_MISO ) ) ) ;
if ( TasmotaGlobal . spi_enabled ) {
2020-06-20 16:58:21 +01:00
if ( PinUsed ( GPIO_SPI_MOSI ) & & PinUsed ( GPIO_SPI_MISO ) ) {
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( " SPI: Using GPIO%02d(MISO), GPIO%02d(MOSI) and GPIO%02d(CLK) " ) ,
2020-06-20 16:58:21 +01:00
Pin ( GPIO_SPI_MISO ) , Pin ( GPIO_SPI_MOSI ) , Pin ( GPIO_SPI_CLK ) ) ;
}
else if ( PinUsed ( GPIO_SPI_MOSI ) ) {
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( " SPI: Using GPIO%02d(MOSI) and GPIO%02d(CLK) " ) ,
2020-06-20 16:58:21 +01:00
Pin ( GPIO_SPI_MOSI ) , Pin ( GPIO_SPI_CLK ) ) ;
}
else if ( PinUsed ( GPIO_SPI_MISO ) ) {
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( " SPI: Using GPIO%02d(MISO) and GPIO%02d(CLK) " ) ,
2020-06-20 16:58:21 +01:00
Pin ( GPIO_SPI_MISO ) , Pin ( GPIO_SPI_CLK ) ) ;
}
}
}
2020-05-03 17:37:12 +01:00
# endif // USE_SPI
2020-11-28 11:46:17 +00:00
# endif // ESP32
2020-10-30 11:29:48 +00:00
TasmotaGlobal . soft_spi_enabled = ( PinUsed ( GPIO_SSPI_SCLK ) & & ( PinUsed ( GPIO_SSPI_MOSI ) | | PinUsed ( GPIO_SSPI_MISO ) ) ) ;
2019-12-02 09:44:27 +00:00
2020-10-30 11:29:48 +00:00
for ( uint32_t i = 0 ; i < ARRAY_SIZE ( TasmotaGlobal . my_module . io ) ; i + + ) {
uint32_t mpin = ValidPin ( i , TasmotaGlobal . my_module . io [ i ] ) ;
2020-11-06 16:09:13 +00:00
// AddLog_P(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 ) {
pinMode ( i , OUTPUT ) ;
digitalWrite ( i , 1 ) ;
}
else if ( AGPIO ( GPIO_OUTPUT_LO ) = = mpin ) {
pinMode ( i , OUTPUT ) ;
digitalWrite ( i , 0 ) ;
}
// 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
2020-03-25 10:29:46 +00:00
if ( ! ( ( 1 = = i ) | | ( 3 = = i ) ) ) { // Skip serial
pinMode ( 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 ) {
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
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
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-08-02 11:40:15 +01:00
# ifdef ESP8266
2020-04-26 16:33:27 +01:00
pinMode ( Pin ( GPIO_PWM1 , i ) , OUTPUT ) ;
2020-11-28 11:46:17 +00:00
# endif // ESP8266
# ifdef ESP32
2020-08-02 11:40:15 +01:00
analogAttach ( Pin ( GPIO_PWM1 , i ) , i ) ;
2020-11-28 11:46:17 +00:00
# endif // ESP32
2020-10-30 11:29:48 +00:00
if ( TasmotaGlobal . light_type ) {
2019-12-10 21:00:38 +00:00
// force PWM GPIOs to low or high mode, see #7165
2020-10-30 11:29:48 +00:00
analogWrite ( Pin ( GPIO_PWM1 , i ) , bitRead ( TasmotaGlobal . pwm_inverted , i ) ? Settings . pwm_range : 0 ) ;
2019-12-10 21:00:38 +00:00
} else {
2020-10-30 11:29:48 +00:00
TasmotaGlobal . pwm_present = true ;
analogWrite ( Pin ( GPIO_PWM1 , i ) , bitRead ( TasmotaGlobal . 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 ) ;
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 ) {
2020-10-28 18:03:39 +00:00
digitalWrite ( Pin ( GPIO_REL1 , i ) , bitRead ( TasmotaGlobal . rel_inverted , i ) ? 1 : 0 ) ;
2020-10-30 11:29:48 +00:00
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-04-26 16:33:27 +01:00
pinMode ( Pin ( GPIO_LED1 , i ) , OUTPUT ) ;
2020-10-30 11:29:48 +00:00
TasmotaGlobal . leds_present + + ;
digitalWrite ( Pin ( GPIO_LED1 , i ) , bitRead ( TasmotaGlobal . 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 ) ;
2020-10-30 11:29:48 +00:00
digitalWrite ( Pin ( GPIO_LEDLNK ) , 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
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 ) ;
2020-08-03 17:08:49 +01:00
XsnsCall ( FUNC_PRE_INIT ) ;
2019-12-02 09:44:27 +00:00
}