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 {
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 ) ;
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
# endif // ESP8266
2020-05-26 17:20:46 +01:00
else
2020-04-10 17:24:08 +01:00
{
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 ;
}
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 ( )
{
for ( uint32_t i = 0 ; i < leds_present ; i + + ) {
SetLedPowerIdx ( i , bitRead ( led_power , i ) ) ;
}
}
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-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
analogWrite ( Pin ( GPIO_LED1 , led ) , bitRead ( led_inverted , led ) ? Settings . pwm_range - pwm : pwm ) ;
} else {
DigitalWrite ( GPIO_LED1 , led , bitRead ( led_inverted , led ) ? ! state : state ) ;
}
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-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 )
{
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
2020-05-27 04:07:25 +01:00
if ( SRC_REMOTE ! = source & & SRC_RETRY ! = source ) {
if ( Settings . flag4 . remote_device_mode ) // SetOption88 - Enable relays in separate device groups
2020-05-30 00:55:33 +01:00
SendDeviceGroupMessage ( device - 1 , DGR_MSGTYP_UPDATE , DGR_ITEM_POWER , ( power > > ( device - 1 ) ) & 1 | 0x01000000 ) ; // Explicitly set number of relays to one
2020-05-27 04:07:25 +01:00
else
SendLocalDeviceGroupMessage ( DGR_MSGTYP_UPDATE , DGR_ITEM_POWER , power ) ;
}
2020-02-21 15:09:21 +00:00
# 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
2020-05-23 16:07:52 +01:00
bool in_byte_is_delimiter = // Char is delimiter when...
( ( ( Settings . serial_delimiter < 128 ) & & ( serial_in_byte = = Settings . serial_delimiter ) ) | | // Any char between 1 and 127 and being delimiter
( ( Settings . serial_delimiter = = 128 ) & & ! isprint ( serial_in_byte ) ) ) & & // Any char not between 32 and 127
! Settings . flag . mqtt_serial_raw ; // In raw mode (CMND_SERIALSEND3) there is never a delimiter
2019-12-02 09:44:27 +00:00
if ( ( 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
2019-12-02 09:44:27 +00:00
serial_in_buffer [ serial_in_byte_counter + + ] = serial_in_byte ;
2020-05-23 16:30:48 +01:00
}
2020-05-26 11:35:21 +01:00
2020-05-23 16:30:48 +01:00
if ( ( serial_in_byte_counter > = INPUT_BUFFER_SIZE - 1 ) | | // Send message when buffer is full or ...
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
\ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
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
2020-01-14 14:58:56 +00:00
bool assume_json = ( ! Settings . flag . mqtt_serial_raw & & ( 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 ) {
ResponseAppend_P ( serial_in_buffer ) ;
} else {
ResponseAppend_P ( PSTR ( " \" " ) ) ;
if ( Settings . flag . mqtt_serial_raw ) {
char hex_char [ ( serial_in_byte_counter * 2 ) + 2 ] ;
ResponseAppend_P ( ToHex_P ( ( unsigned char * ) serial_in_buffer , serial_in_byte_counter , hex_char , sizeof ( hex_char ) ) ) ;
} else {
ResponseAppend_P ( EscapeJSONString ( serial_in_buffer ) . c_str ( ) ) ;
}
ResponseAppend_P ( PSTR ( " \" " ) ) ;
}
2020-05-26 11:35:21 +01:00
ResponseJsonEnd ( ) ;
2020-05-24 16:15:06 +01:00
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-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 ) ) ) {
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 ) ;
}