2019-09-29 17:00:01 +01:00
/*
2023-03-29 14:43:19 +01:00
xdrv_27_Shutter . ino - Shutter / Blind support for Tasmota
2019-09-29 17:00:01 +01:00
2024-01-16 15:35:19 +00:00
Copyright ( C ) 2023 Stefan Bode
2019-09-29 17:00:01 +01: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/>.
*/
2023-03-29 14:43:19 +01:00
# ifdef ESP8266
2019-09-30 10:21:43 +01:00
# ifdef USE_SHUTTER
2019-09-29 17:00:01 +01:00
/*********************************************************************************************\
2019-09-30 10:21:43 +01:00
* Shutter or Blind support using two consecutive relays
2019-09-29 17:00:01 +01:00
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# define XDRV_27 27
2020-09-05 19:39:24 +01:00
# ifndef SHUTTER_STEPPER
# define SHUTTER_STEPPER
# endif
2019-09-29 17:00:01 +01:00
2022-02-04 07:58:42 +00:00
# ifndef SHUTTER_RELAY_OPERATION_TIME
2022-02-04 08:13:02 +00:00
# define SHUTTER_RELAY_OPERATION_TIME 100 // wait for direction relay 0.1sec before power up main relay
2022-02-04 07:58:42 +00:00
# endif
2021-02-18 11:42:59 +00:00
//#define SHUTTER_UNITTEST
2019-09-29 17:00:01 +01:00
# define D_SHUTTER "SHUTTER"
2022-02-04 07:58:42 +00:00
2020-09-09 07:58:00 +01:00
const uint16_t RESOLUTION = 1000 ; // incresed to 1000 in 8.5 to ramp servos
2020-09-09 13:04:57 +01:00
const uint8_t STEPS_PER_SECOND = 20 ; // FUNC_EVERY_50_MSECOND
2021-09-15 09:08:09 +01:00
const uint16_t pwm_servo_max = 500 ;
const uint16_t pwm_servo_min = 90 ;
2019-12-04 11:34:27 +00:00
2019-10-09 05:36:11 +01:00
uint8_t calibrate_pos [ 6 ] = { 0 , 30 , 50 , 70 , 90 , 100 } ;
uint16_t messwerte [ 5 ] = { 30 , 50 , 70 , 90 , 100 } ;
2020-09-08 18:34:10 +01:00
2020-09-09 07:58:00 +01:00
int32_t velocity_max = 0 ;
int32_t velocity_change_per_step_max = 0 ;
2020-09-08 18:34:10 +01:00
int32_t min_runtime_ms = 0 ;
2020-09-09 07:58:00 +01:00
int32_t current_stop_way = 0 ;
int32_t next_possible_stop_position = 0 ;
2021-02-22 15:54:58 +00:00
int32_t current_real_position = 0 ;
int32_t current_pwm_velocity = 0 ;
2019-10-09 05:36:11 +01:00
2023-05-21 11:25:11 +01:00
const char HTTP_MSG_SLIDER_SHUTTER [ ] PROGMEM =
2024-01-16 15:35:19 +00:00
" <tr><td colspan=2> "
2023-05-21 11:25:11 +01:00
" <div><span class='p'>%s</span><span class='q'>%s</span></div> "
2024-01-16 15:35:19 +00:00
" <div><input type='range' min='0' max='100' value='%d' onchange='lc( \" u \" ,%d,value)'></div> "
" {e} " ;
2023-05-21 11:25:11 +01:00
2022-12-14 10:25:41 +00:00
const uint8_t MAX_MODES = 8 ;
enum Shutterposition_mode { SHT_UNDEF , SHT_TIME , SHT_TIME_UP_DOWN , SHT_TIME_GARAGE , SHT_COUNTER , SHT_PWM_VALUE , SHT_PWM_TIME , SHT_AUTOCONFIG } ;
2020-09-09 07:58:00 +01:00
enum Shutterswitch_mode { SHT_SWITCH , SHT_PULSE , } ;
2020-03-02 19:32:55 +00:00
enum ShutterButtonStates { SHT_NOT_PRESSED , SHT_PRESSED_MULTI , SHT_PRESSED_HOLD , SHT_PRESSED_IMMEDIATE , SHT_PRESSED_EXT_HOLD , SHT_PRESSED_MULTI_SIMULTANEOUS , SHT_PRESSED_HOLD_SIMULTANEOUS , SHT_PRESSED_EXT_HOLD_SIMULTANEOUS , } ;
2019-09-29 17:00:01 +01:00
const char kShutterCommands [ ] PROGMEM = D_PRFX_SHUTTER " | "
2020-07-21 06:24:14 +01:00
D_CMND_SHUTTER_OPEN " | " D_CMND_SHUTTER_CLOSE " | " D_CMND_SHUTTER_TOGGLE " | " D_CMND_SHUTTER_TOGGLEDIR " | " D_CMND_SHUTTER_STOP " | " D_CMND_SHUTTER_POSITION " | "
2020-09-09 14:24:21 +01:00
D_CMND_SHUTTER_OPENTIME " | " D_CMND_SHUTTER_CLOSETIME " | " D_CMND_SHUTTER_RELAY " | " D_CMND_SHUTTER_MODE " | " D_CMND_SHUTTER_PWMRANGE " | "
2020-04-21 15:20:20 +01:00
D_CMND_SHUTTER_SETHALFWAY " | " D_CMND_SHUTTER_SETCLOSE " | " D_CMND_SHUTTER_SETOPEN " | " D_CMND_SHUTTER_INVERT " | " D_CMND_SHUTTER_CLIBRATION " | "
2020-04-11 07:28:05 +01:00
D_CMND_SHUTTER_MOTORDELAY " | " D_CMND_SHUTTER_FREQUENCY " | " D_CMND_SHUTTER_BUTTON " | " D_CMND_SHUTTER_LOCK " | " D_CMND_SHUTTER_ENABLEENDSTOPTIME " | " D_CMND_SHUTTER_INVERTWEBBUTTONS " | "
2021-02-18 11:42:59 +00:00
D_CMND_SHUTTER_STOPOPEN " | " D_CMND_SHUTTER_STOPCLOSE " | " D_CMND_SHUTTER_STOPTOGGLE " | " D_CMND_SHUTTER_STOPTOGGLEDIR " | " D_CMND_SHUTTER_STOPPOSITION " | " D_CMND_SHUTTER_INCDEC " | "
2022-12-14 10:25:41 +00:00
D_CMND_SHUTTER_UNITTEST " | " D_CMND_SHUTTER_TILTCONFIG " | " D_CMND_SHUTTER_SETTILT " | " D_CMND_SHUTTER_TILTINCDEC " | " D_CMND_SHUTTER_MOTORSTOP ;
2019-09-29 17:00:01 +01:00
void ( * const ShutterCommand [ ] ) ( void ) PROGMEM = {
2020-07-21 06:24:14 +01:00
& CmndShutterOpen , & CmndShutterClose , & CmndShutterToggle , & CmndShutterToggleDir , & CmndShutterStop , & CmndShutterPosition ,
2020-09-09 14:24:21 +01:00
& CmndShutterOpenTime , & CmndShutterCloseTime , & CmndShutterRelay , & CmndShutterMode , & CmndShutterPwmRange ,
2020-04-21 15:20:20 +01:00
& CmndShutterSetHalfway , & CmndShutterSetClose , & CmndShutterSetOpen , & CmndShutterInvert , & CmndShutterCalibration , & CmndShutterMotorDelay ,
2020-04-11 07:28:05 +01:00
& CmndShutterFrequency , & CmndShutterButton , & CmndShutterLock , & CmndShutterEnableEndStopTime , & CmndShutterInvertWebButtons ,
2021-02-18 11:42:59 +00:00
& CmndShutterStopOpen , & CmndShutterStopClose , & CmndShutterStopToggle , & CmndShutterStopToggleDir , & CmndShutterStopPosition , & CmndShutterIncDec ,
2022-12-14 10:25:41 +00:00
& CmndShutterUnitTest , & CmndShutterTiltConfig , & CmndShutterSetTilt , & CmndShutterTiltIncDec , & CmndShutterMotorStop } ;
2019-09-29 17:00:01 +01:00
2021-11-07 14:53:12 +00:00
const char JSON_SHUTTER_POS [ ] PROGMEM = " \" " D_PRFX_SHUTTER " %d \" :{ \" Position \" :%d, \" Direction \" :%d, \" Target \" :%d, \" Tilt \" :%d} " ;
2020-01-22 12:23:59 +00:00
const char JSON_SHUTTER_BUTTON [ ] PROGMEM = " \" " D_PRFX_SHUTTER " %d \" :{ \" Button%d \" :%d} " ;
2019-09-30 10:21:43 +01:00
# include <Ticker.h>
2019-09-29 17:00:01 +01:00
Ticker TickerShutter ;
2019-09-30 10:21:43 +01:00
struct SHUTTER {
2020-09-09 13:04:57 +01:00
uint32_t time ; // operating time of the shutter in 0.05sec
int32_t open_max ; // max value on maximum open calculated
int32_t target_position ; // position to go to
int32_t start_position ; // position before a movement is started. init at start
int32_t real_position ; // value between 0 and Shutter[i].open_max
uint16_t open_time ; // duration to open the Shutter[i]. 112 = 11.2sec
uint16_t close_time ; // duration to close the Shutter[i]. 112 = 11.2sec
uint16_t close_velocity ; // in relation to open velocity. higher value = faster
int8_t direction ; // 1 == UP , 0 == stop; -1 == down
int8_t lastdirection ; // last direction (1 == UP , -1 == down)
uint8_t switch_mode ; // how to switch relays: SHT_SWITCH, SHT_PULSE
2021-09-03 11:57:51 +01:00
int8_t motordelay ; // initial motorstarttime in 0.05sec. Also uses for ramp at steppers and servos, negative if motor stops late
2020-09-09 13:04:57 +01:00
int16_t pwm_velocity ; // frequency of PWN for stepper motors or PWM duty cycle change for PWM servo
uint16_t pwm_value ; // dutyload of PWM 0..1023 on ESP8266
uint16_t close_velocity_max ; // maximum of PWM change during closeing. Defines velocity on opening. Steppers and Servos only
int32_t accelerator ; // speed of ramp-up, ramp down of shutters with velocity control. Steppers and Servos only
2021-11-07 14:53:12 +00:00
int8_t tilt_config [ 5 ] ; // tilt_min, tilt_max, duration, tilt_closed_value, tilt_opened_value
2021-11-14 21:17:28 +00:00
int8_t tilt_real_pos ; // -90 to 90
int8_t tilt_target_pos ; // target positon for movements of the tilt
2023-03-31 08:46:36 +01:00
int8_t tilt_target_pos_override ; // one time override of automatic calculation of tilt_target
int8_t tilt_start_pos ; // saved start position before shutter moves
uint8_t tilt_velocity ; // degree rotation per step 0.05sec
int8_t tiltmoving ; // 0 operating move, 1 = operating tilt
uint16_t venetian_delay = 0 ; // Delay in steps before venetian shutter start physical moving. Based on tilt position
2021-11-14 21:17:28 +00:00
uint16_t min_realPositionChange = 0 ; // minimum change of the position before the shutter operates. different for PWM and time based operations
uint16_t min_TiltChange = 0 ; // minimum change of the tilt before the shutter operates. different for PWM and time based operations
2022-12-22 16:02:01 +00:00
uint16_t last_reported_time = 0 ; // get information on skipped 50ms loop() slots
uint32_t last_stop_time = 0 ; // record the last time the relay was switched off
2020-09-09 13:04:57 +01:00
} Shutter [ MAX_SHUTTERS ] ;
struct SHUTTERGLOBAL {
power_t RelayShutterMask = 0 ; // bit mask with 11 at the position of relays that belong to at least ONE shutter
power_t RelayOldMask = 0 ; // bitmatrix that contain the last known state of all relays. Required to detemine the manual changed relay.
power_t RelayCurrentMask = 0 ; // bitmatrix that contain the current state of all relays
2023-04-04 15:17:12 +01:00
uint8_t LastChangedRelay = 0 ; // Relay 1..32, 0 no change
2020-09-09 13:04:57 +01:00
uint8_t position_mode = 0 ; // how to calculate actual position: SHT_TIME, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME
uint8_t skip_relay_change ; // avoid overrun at endstops
uint8_t start_reported = 0 ; // indicates of the shutter start was reported through MQTT JSON
2021-11-14 21:17:28 +00:00
uint16_t open_velocity_max = RESOLUTION ; // maximum of PWM change during opening. Defines velocity on opening. Steppers and Servos only
2020-09-09 13:04:57 +01:00
} ShutterGlobal ;
2019-09-30 10:21:43 +01:00
2020-09-05 19:39:24 +01:00
# define SHT_DIV_ROUND(__A, __B) (((__A) + (__B) / 2) / (__B))
2023-04-04 15:17:12 +01:00
uint8_t ShutterGetRelayNoFromBitfield ( power_t number ) {
int position = 0 ;
while ( number ! = 0 ) {
position + + ;
if ( number & 1 ) return position ;
number > > = 1 ;
}
return 0 ; // return 0 if no relay found
}
2023-03-29 14:43:19 +01:00
bool ShutterStatus ( void ) {
if ( Settings - > flag3 . shutter_mode ) { // SetOption80 - (Shutter) Enable shutter support (1)
Response_P ( PSTR ( " { \" " D_CMND_STATUS D_STATUS13_SHUTTER " \" :{ " ) ) ;
for ( uint32_t i = 0 ; i < MAX_SHUTTERS ; i + + ) {
if ( 0 = = Settings - > shutter_startrelay [ i ] ) { break ; }
if ( i > 0 ) { ResponseAppend_P ( PSTR ( " , " ) ) ; }
ResponseAppend_P ( PSTR ( " \" " D_STATUS13_SHUTTER " %d \" :{ \" Relay1 \" :%d, \" Relay2 \" :%d, \" Open \" :%d, \" Close \" :%d, "
" \" 50perc \" :%d, \" Delay \" :%d, \" Opt \" : \" %s \" , "
" \" Calib \" :[%d,%d,%d,%d,%d], "
" \" Mode \" : \" %d \" } " ) ,
i , Settings - > shutter_startrelay [ i ] , Settings - > shutter_startrelay [ i ] + 1 , Settings - > shutter_opentime [ i ] , Settings - > shutter_closetime [ i ] ,
Settings - > shutter_set50percent [ i ] , Settings - > shutter_motordelay [ i ] , GetBinary8 ( Settings - > shutter_options [ i ] , 4 ) . c_str ( ) ,
Settings - > shuttercoeff [ 0 ] [ i ] , Settings - > shuttercoeff [ 1 ] [ i ] , Settings - > shuttercoeff [ 2 ] [ i ] , Settings - > shuttercoeff [ 3 ] [ i ] , Settings - > shuttercoeff [ 4 ] [ i ] ,
Settings - > shutter_mode ) ;
}
ResponseJsonEndEnd ( ) ;
return true ;
}
return false ;
}
2022-05-13 18:11:00 +01:00
2020-01-04 14:09:57 +00:00
void ShutterLogPos ( uint32_t i )
{
char stemp2 [ 10 ] ;
2020-09-09 13:04:57 +01:00
dtostrfd ( ( float ) Shutter [ i ] . time / STEPS_PER_SECOND , 2 , stemp2 ) ;
2021-11-07 14:53:12 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " SHT: Shtr%d Real %d, Start %d, Stop %d, Dir %d, Delay %d, Rtc %s [s], Freq %d, PWM %d, Tilt %d " ) ,
i + 1 , Shutter [ i ] . real_position , Shutter [ i ] . start_position , Shutter [ i ] . target_position , Shutter [ i ] . direction , Shutter [ i ] . motordelay , stemp2 ,
Shutter [ i ] . pwm_velocity , Shutter [ i ] . pwm_value , Shutter [ i ] . tilt_real_pos ) ;
2020-09-05 19:39:24 +01:00
}
2023-04-01 13:53:01 +01:00
uint8_t ShutterGetStartRelay ( uint8_t index ) {
return Settings - > shutter_startrelay [ index ] ;
}
2023-05-21 11:25:11 +01:00
uint8_t ShutterGetOptions ( uint8_t index ) {
return Settings - > shutter_options [ index ] ;
}
2023-04-05 12:47:12 +01:00
int8_t ShutterGetTiltConfig ( uint8_t config_idx , uint8_t index ) {
return Shutter [ index ] . tilt_config [ config_idx ] ;
}
2020-09-05 19:39:24 +01:00
void ExecuteCommandPowerShutter ( uint32_t device , uint32_t state , uint32_t source )
{
2020-09-09 07:58:00 +01:00
// first implementation for virtual relays. Avoid switching relay numbers that do not exist.
2020-10-30 11:29:48 +00:00
if ( device < = TasmotaGlobal . devices_present ) ExecuteCommandPower ( device , state , source ) ;
2020-09-05 19:39:24 +01:00
}
void ShutterUpdateVelocity ( uint8_t i )
{
2020-09-09 07:58:00 +01:00
// No Logging allowed. Part of RTC Timer
// will be calles through RTC every 50ms.
2021-02-22 15:54:58 +00:00
// do not allow accellerator to stop movement
Shutter [ i ] . pwm_velocity = tmax ( velocity_change_per_step_max , Shutter [ i ] . pwm_velocity + Shutter [ i ] . accelerator ) ;
Shutter [ i ] . pwm_velocity = tmin ( Shutter [ i ] . direction = = 1 ? ShutterGlobal . open_velocity_max : Shutter [ i ] . close_velocity_max , Shutter [ i ] . pwm_velocity ) ;
2021-09-15 09:08:09 +01:00
// respect hard coded SDK limit of PWM_MIN on PWM frequency.
2021-02-18 11:42:59 +00:00
if ( ShutterGlobal . position_mode = = SHT_COUNTER ) {
2021-09-15 09:08:09 +01:00
Shutter [ i ] . pwm_velocity = tmax ( PWM_MIN , Shutter [ i ] . pwm_velocity ) ;
2021-02-18 11:42:59 +00:00
}
2020-01-04 14:09:57 +00:00
}
2019-09-30 10:21:43 +01:00
void ShutterRtc50mS ( void )
2019-09-29 17:00:01 +01:00
{
2022-08-01 18:27:49 +01:00
# ifdef ESP32
bool pwm_apply = false ; // ESP32 only, do we need to apply PWM changes
# endif
2020-09-09 07:58:00 +01:00
// No Logging allowed. RTC Timer
2020-10-30 11:29:48 +00:00
for ( uint8_t i = 0 ; i < TasmotaGlobal . shutters_present ; i + + ) {
2020-09-09 13:04:57 +01:00
if ( Shutter [ i ] . direction ) {
2020-09-08 18:34:10 +01:00
// update position data before increasing counter
2020-09-09 13:04:57 +01:00
Shutter [ i ] . real_position = ShutterCalculatePosition ( i ) ;
Shutter [ i ] . time + + ;
2020-09-08 18:34:10 +01:00
ShutterCalculateAccelerator ( i ) ;
2020-09-09 13:04:57 +01:00
switch ( ShutterGlobal . position_mode ) {
2020-09-08 18:34:10 +01:00
case SHT_PWM_VALUE :
2020-09-05 19:39:24 +01:00
ShutterUpdateVelocity ( i ) ;
2020-09-09 13:04:57 +01:00
Shutter [ i ] . real_position + = Shutter [ i ] . direction > 0 ? Shutter [ i ] . pwm_velocity : ( Shutter [ i ] . direction < 0 ? - Shutter [ i ] . pwm_velocity : 0 ) ;
2021-06-11 17:14:12 +01:00
Shutter [ i ] . pwm_value = SHT_DIV_ROUND ( ( Settings - > shutter_pwmrange [ 1 ] [ i ] - Settings - > shutter_pwmrange [ 0 ] [ i ] ) * Shutter [ i ] . real_position , Shutter [ i ] . open_max ) + Settings - > shutter_pwmrange [ 0 ] [ i ] ;
2024-01-07 14:10:19 +00:00
AnalogWrite ( Pin ( GPIO_PWM1 , i ) , Shutter [ i ] . pwm_value ) ;
2020-09-08 18:34:10 +01:00
break ;
case SHT_COUNTER :
2020-09-09 13:04:57 +01:00
if ( Shutter [ i ] . accelerator ) {
2021-01-23 15:26:23 +00:00
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Accelerator i=%d -> %d"),i, Shutter[i].accelerator);
2020-09-08 18:34:10 +01:00
ShutterUpdateVelocity ( i ) ;
2022-05-13 18:11:00 +01:00
digitalWrite ( Pin ( GPIO_PWM1 , i ) , LOW ) ;
2022-02-11 17:44:37 +00:00
# ifdef ESP8266
2022-02-11 17:05:44 +00:00
// Convert frequency into clock cycles
uint32_t cc = microsecondsToClockCycles ( 1000000UL ) / Shutter [ i ] . pwm_velocity ;
startWaveformClockCycles ( Pin ( GPIO_PWM1 , i ) , cc / 2 , cc / 2 , 0 , - 1 , 0 , false ) ;
2022-02-11 17:44:37 +00:00
# endif // ESP8266
# ifdef ESP32
2022-07-22 13:48:08 +01:00
analogWriteFreq ( Shutter [ i ] . pwm_velocity , Pin ( GPIO_PWM1 , i ) ) ;
TasmotaGlobal . pwm_value [ i ] = 512 ;
2022-08-01 18:27:49 +01:00
pwm_apply = true ;
2022-02-11 17:44:37 +00:00
# endif // ESP32
2020-09-08 18:34:10 +01:00
}
break ;
}
2020-09-09 13:04:57 +01:00
} // if (Shutter[i].direction)
2019-09-29 17:00:01 +01:00
}
2022-08-01 18:27:49 +01:00
# ifdef ESP32
if ( pwm_apply ) { PwmApplyGPIO ( false ) ; }
# endif
2019-09-29 17:00:01 +01:00
}
2020-12-28 11:27:45 +00:00
int32_t ShutterPercentToRealPosition ( int16_t percent , uint32_t index )
2019-09-29 17:00:01 +01:00
{
2021-06-11 17:14:12 +01:00
if ( Settings - > shutter_set50percent [ index ] ! = 50 ) {
return ( percent < = 5 ) ? Settings - > shuttercoeff [ 2 ] [ index ] * percent * 10 : ( Settings - > shuttercoeff [ 1 ] [ index ] * percent + ( Settings - > shuttercoeff [ 0 ] [ index ] * 10 ) ) * 10 ;
2019-09-29 17:00:01 +01:00
} else {
2020-12-15 16:44:06 +00:00
int64_t realpos ;
2019-10-09 05:36:11 +01:00
// check against DIV 0
2020-01-04 14:09:57 +00:00
for ( uint32_t j = 0 ; j < 5 ; j + + ) {
2021-06-11 17:14:12 +01:00
if ( 0 = = Settings - > shuttercoeff [ j ] [ index ] ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_ERROR , PSTR ( " SHT: RESET/INIT CALIBRATION MATRIX DIV 0 " ) ) ;
2020-01-04 14:09:57 +00:00
for ( uint32_t k = 0 ; k < 5 ; k + + ) {
2021-06-11 17:14:12 +01:00
Settings - > shuttercoeff [ k ] [ index ] = SHT_DIV_ROUND ( calibrate_pos [ k + 1 ] * 1000 , calibrate_pos [ 5 ] ) ;
2019-10-09 05:36:11 +01:00
}
}
}
2020-09-09 13:04:57 +01:00
for ( uint32_t k = 0 ; k < 5 ; k + + ) {
2021-06-11 17:14:12 +01:00
if ( ( percent * 10 ) > = Settings - > shuttercoeff [ k ] [ index ] ) {
2020-09-09 13:04:57 +01:00
realpos = SHT_DIV_ROUND ( Shutter [ index ] . open_max * calibrate_pos [ k + 1 ] , 100 ) ;
2021-06-11 17:14:12 +01:00
//AddLog(LOG_LEVEL_ERROR, PSTR("SHT: Realposition TEMP1: %d, %d %%, coeff %d"), realpos, percent, Settings->shuttercoeff[k][index]);
2019-10-09 05:36:11 +01:00
} else {
2021-02-18 11:42:59 +00:00
//AddLog(LOG_LEVEL_ERROR, PSTR("SHT: Shutter[%d].open_max: %d"),index, Shutter[index].open_max);
2020-09-09 13:04:57 +01:00
if ( 0 = = k ) {
2021-06-11 17:14:12 +01:00
realpos = SHT_DIV_ROUND ( ( int64_t ) percent * Shutter [ index ] . open_max * calibrate_pos [ k + 1 ] , Settings - > shuttercoeff [ k ] [ index ] * 10 ) ;
//AddLog(LOG_LEVEL_ERROR, PSTR("SHT: Realposition TEMP3: %d, %d %%, coeff %d"), realpos, percent, Settings->shuttercoeff[k][index]);
2019-10-09 05:36:11 +01:00
} else {
2021-06-11 17:14:12 +01:00
//uint32_t addon = ( percent*10 - Settings->shuttercoeff[k-1][index] ) * Shutter[index].open_max * (calibrate_pos[k+1] - calibrate_pos[k]) / (Settings->shuttercoeff[k][index] -Settings->shuttercoeff[k-1][index]) / 100;
//AddLog(LOG_LEVEL_ERROR, PSTR("SHT: Realposition TEMP2: %d, %d %%, coeff %d"), addon, (calibrate_pos[k+1] - calibrate_pos[k]), (Settings->shuttercoeff[k][index] -Settings->shuttercoeff[k-1][index]));
realpos + = SHT_DIV_ROUND ( ( ( int64_t ) percent * 10 - Settings - > shuttercoeff [ k - 1 ] [ index ] ) * Shutter [ index ] . open_max * ( calibrate_pos [ k + 1 ] - calibrate_pos [ k ] ) , ( Settings - > shuttercoeff [ k ] [ index ] - Settings - > shuttercoeff [ k - 1 ] [ index ] ) * 100 ) ;
2019-10-09 05:36:11 +01:00
}
break ;
}
}
2020-12-28 11:27:45 +00:00
return realpos < 0 ? 0 : realpos ;
2019-09-29 17:00:01 +01:00
}
}
2020-12-28 11:27:45 +00:00
uint8_t ShutterRealToPercentPosition ( int32_t realpos , uint32_t index )
2019-09-29 17:00:01 +01:00
{
2023-05-21 11:25:11 +01:00
int64_t realpercent ;
2023-04-01 13:53:01 +01:00
if ( realpos = = - 9999 ) {
realpos = Shutter [ index ] . real_position ;
}
2021-06-11 17:14:12 +01:00
if ( Settings - > shutter_set50percent [ index ] ! = 50 ) {
2023-05-21 11:25:11 +01:00
realpercent = ( Settings - > shuttercoeff [ 2 ] [ index ] * 5 > realpos / 10 ) ? SHT_DIV_ROUND ( realpos / 10 , Settings - > shuttercoeff [ 2 ] [ index ] ) : SHT_DIV_ROUND ( realpos / 10 - Settings - > shuttercoeff [ 0 ] [ index ] * 10 , Settings - > shuttercoeff [ 1 ] [ index ] ) ;
2019-09-29 17:00:01 +01:00
} else {
2020-09-09 13:04:57 +01:00
for ( uint32_t j = 0 ; j < 5 ; j + + ) {
if ( realpos > = Shutter [ index ] . open_max * calibrate_pos [ j + 1 ] / 100 ) {
2021-06-11 17:14:12 +01:00
realpercent = SHT_DIV_ROUND ( Settings - > shuttercoeff [ j ] [ index ] , 10 ) ;
2021-02-18 11:42:59 +00:00
//AddLog(LOG_LEVEL_ERROR, PSTR("SHT: Realpercent TEMP1: %d %%, %d, coeff %d"), realpercent, realpos, Shutter[index].open_max * calibrate_pos[j+1] / 100);
2019-10-09 05:36:11 +01:00
} else {
2021-02-18 11:42:59 +00:00
//AddLog(LOG_LEVEL_ERROR, PSTR("SHT: Shutter[%d].open_max: %d"),index, Shutter[index].open_max);
2020-09-09 13:04:57 +01:00
if ( 0 = = j ) {
2021-06-11 17:14:12 +01:00
realpercent = SHT_DIV_ROUND ( ( ( int64_t ) realpos - SHT_DIV_ROUND ( Shutter [ index ] . open_max * calibrate_pos [ j ] , 100 ) ) * Settings - > shuttercoeff [ j ] [ index ] , calibrate_pos [ j + 1 ] / 10 * Shutter [ index ] . open_max ) ;
2019-10-09 05:36:11 +01:00
} else {
2021-06-11 17:14:12 +01:00
//uint16_t addon = ( realpos - (Shutter[index].open_max * calibrate_pos[j] / 100) ) * 10 * (Settings->shuttercoeff[j][index] - Settings->shuttercoeff[j-1][index]) / (calibrate_pos[j+1] - calibrate_pos[j])/Shutter[index].open_max;
//uint16_t addon = ( realpercent*10 - Settings->shuttercoeff[j-1][index] ) * Shutter[index].open_max * (calibrate_pos[j+1] - calibrate_pos[j]) / (Settings->shuttercoeff[j][index] -Settings->shuttercoeff[j-1][index]) / 100;
//AddLog(LOG_LEVEL_ERROR, PSTR("SHT: Realpercent TEMP2: %d %%, delta %d, %d, coeff %d"), addon,( realpos - (Shutter[index].open_max * calibrate_pos[j] / 100) ) , (calibrate_pos[j+1] - calibrate_pos[j])* Shutter[index].open_max/100, (Settings->shuttercoeff[j][index] -Settings->shuttercoeff[j-1][index]));
realpercent + = SHT_DIV_ROUND ( ( ( int64_t ) realpos - SHT_DIV_ROUND ( Shutter [ index ] . open_max * calibrate_pos [ j ] , 100 ) ) * ( Settings - > shuttercoeff [ j ] [ index ] - Settings - > shuttercoeff [ j - 1 ] [ index ] ) , ( calibrate_pos [ j + 1 ] - calibrate_pos [ j ] ) / 10 * Shutter [ index ] . open_max ) ;
2022-12-10 22:52:53 +00:00
}
2019-10-09 05:36:11 +01:00
break ;
}
}
}
2023-08-03 10:55:20 +01:00
realpercent = realpercent < 0 ? 0 : realpercent ;
return realpercent ;
2019-09-29 17:00:01 +01:00
}
2019-09-30 10:21:43 +01:00
void ShutterInit ( void )
2019-09-29 17:00:01 +01:00
{
2020-10-30 11:29:48 +00:00
TasmotaGlobal . shutters_present = 0 ;
2020-09-09 13:04:57 +01:00
ShutterGlobal . RelayShutterMask = 0 ;
2019-09-29 17:00:01 +01:00
//Initialize to get relay that changed
2020-10-28 18:03:39 +00:00
ShutterGlobal . RelayOldMask = TasmotaGlobal . power ;
2020-09-09 07:58:00 +01:00
2019-09-29 17:00:01 +01:00
2019-12-20 10:27:35 +00:00
// if shutter 4 is unused
2021-06-11 17:14:12 +01:00
if ( Settings - > shutter_startrelay [ MAX_SHUTTERS - 1 ] = = 0 ) {
ShutterGlobal . open_velocity_max = Settings - > shuttercoeff [ 4 ] [ 3 ] > 0 ? Settings - > shuttercoeff [ 4 ] [ 3 ] : ShutterGlobal . open_velocity_max ;
2019-12-20 10:27:35 +00:00
}
2019-09-30 10:21:43 +01:00
for ( uint32_t i = 0 ; i < MAX_SHUTTERS ; i + + ) {
2019-09-29 17:00:01 +01:00
// set startrelay to 1 on first init, but only to shutter 1. 90% usecase
2022-12-14 10:25:41 +00:00
if ( Settings - > shutter_startrelay [ i ] & & ( Settings - > shutter_startrelay [ i ] < = 32 ) ) {
bool relay_in_interlock = false ;
2020-10-30 11:29:48 +00:00
TasmotaGlobal . shutters_present + + ;
2019-09-29 17:00:01 +01:00
2020-09-09 07:58:00 +01:00
// Add the two relays to the mask to knaw they belong to shutters
2021-06-11 17:14:12 +01:00
ShutterGlobal . RelayShutterMask | = 3 < < ( Settings - > shutter_startrelay [ i ] - 1 ) ;
2019-09-29 17:00:01 +01:00
2020-09-09 07:58:00 +01:00
// All shutters must have same mode. Switch OR Pulse. N
2021-06-11 17:14:12 +01:00
switch ( Settings - > pulse_timer [ i ] ) {
2020-09-05 19:39:24 +01:00
case 0 :
2020-09-09 13:04:57 +01:00
Shutter [ i ] . switch_mode = SHT_SWITCH ;
2020-09-05 19:39:24 +01:00
break ;
default :
2020-09-09 13:04:57 +01:00
Shutter [ i ] . switch_mode = SHT_PULSE ;
2020-09-05 19:39:24 +01:00
break ;
}
2022-12-14 10:25:41 +00:00
// Check if the relay is in an INTERLOCK group. required to set the right mode or
// verify that on SHT_TIME INTERLOCK is set
for ( uint32_t j = 0 ; j < MAX_INTERLOCKS * Settings - > flag . interlock ; j + + ) { // CMND_INTERLOCK - Enable/disable interlock
//AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Interlock state i=%d %d, flag %d, Shuttermask %d, MaskedIL %d"),i, Settings->interlock[i], Settings->flag.interlock,ShutterGlobal.RelayShutterMask, Settings->interlock[i]&ShutterGlobal.RelayShutterMask);
if ( Settings - > interlock [ j ] & & ( Settings - > interlock [ j ] & ShutterGlobal . RelayShutterMask ) ) {
//AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Relay in Interlock group"));
relay_in_interlock = true ;
2020-09-09 07:58:00 +01:00
}
2022-12-14 10:25:41 +00:00
}
2020-09-09 07:58:00 +01:00
2022-12-14 10:25:41 +00:00
if ( Settings - > shutter_mode = = SHT_AUTOCONFIG | | Settings - > shutter_mode = = SHT_UNDEF ) {
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: Mode undef.. calculate... " ) ) ;
ShutterGlobal . position_mode = SHT_TIME ;
if ( ! relay_in_interlock ) {
2022-12-21 14:26:42 +00:00
// temporary to maintain old functionality
2022-12-14 10:25:41 +00:00
if ( Settings - > shutter_mode = = SHT_UNDEF ) {
ShutterGlobal . position_mode = SHT_TIME_UP_DOWN ;
}
2020-09-09 13:04:57 +01:00
if ( PinUsed ( GPIO_PWM1 , i ) & & PinUsed ( GPIO_CNTR1 , i ) ) {
ShutterGlobal . position_mode = SHT_COUNTER ;
}
2019-09-29 17:00:01 +01:00
}
} else {
2021-06-11 17:14:12 +01:00
ShutterGlobal . position_mode = Settings - > shutter_mode ;
2019-09-29 17:00:01 +01:00
}
2022-12-14 10:25:41 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " SHT: ShutterMode: %d " ) , ShutterGlobal . position_mode ) ;
2020-09-09 07:58:00 +01:00
// main function for stepper and servos to control velocity and acceleration.
2019-09-30 10:21:43 +01:00
TickerShutter . attach_ms ( 50 , ShutterRtc50mS ) ;
2020-09-09 07:58:00 +01:00
2019-09-29 17:00:01 +01:00
// default the 50 percent should not have any impact without changing it. set to 60
2021-06-11 17:14:12 +01:00
Settings - > shutter_set50percent [ i ] = ( Settings - > shutter_set50percent [ i ] > 0 ) ? Settings - > shutter_set50percent [ i ] : 50 ;
2019-10-09 05:36:11 +01:00
2019-09-29 17:00:01 +01:00
// use 10 sec. as default to allow everybody to play without deep initialize
2021-06-11 17:14:12 +01:00
Shutter [ i ] . open_time = Settings - > shutter_opentime [ i ] = ( Settings - > shutter_opentime [ i ] > 0 ) ? Settings - > shutter_opentime [ i ] : 100 ;
Shutter [ i ] . close_time = Settings - > shutter_closetime [ i ] = ( Settings - > shutter_closetime [ i ] > 0 ) ? Settings - > shutter_closetime [ i ] : 100 ;
2019-09-29 17:00:01 +01:00
2020-09-08 18:34:10 +01:00
// Update Calculation 20 because time interval is 0.05 sec ans time is in 0.1sec
2020-09-09 13:04:57 +01:00
Shutter [ i ] . open_max = STEPS_PER_SECOND * RESOLUTION * Shutter [ i ] . open_time / 10 ;
Shutter [ i ] . close_velocity = Shutter [ i ] . open_max / Shutter [ i ] . close_time / 2 ;
2020-09-05 19:39:24 +01:00
2019-09-29 17:00:01 +01:00
// calculate a ramp slope at the first 5 percent to compensate that shutters move with down part later than the upper part
2021-06-11 17:14:12 +01:00
if ( Settings - > shutter_set50percent [ i ] ! = 50 ) {
Settings - > shuttercoeff [ 1 ] [ i ] = Shutter [ i ] . open_max / 10 * ( 100 - Settings - > shutter_set50percent [ i ] ) / 5000 ;
Settings - > shuttercoeff [ 0 ] [ i ] = Shutter [ i ] . open_max / 100 - ( Settings - > shuttercoeff [ 1 ] [ i ] * 10 ) ;
Settings - > shuttercoeff [ 2 ] [ i ] = ( int32_t ) ( Settings - > shuttercoeff [ 0 ] [ i ] * 10 + 5 * Settings - > shuttercoeff [ 1 ] [ i ] ) / 5 ;
//AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Shtr%d Shutter[i].open_max %d, 50perc:%d, 0:%d, 1:%d 2:%d"), i, Shutter[i].open_max, Settings->shutter_set50percent[i], Settings->shuttercoeff[0][i],Settings->shuttercoeff[1][i],Settings->shuttercoeff[2][i]);
2020-10-05 11:45:08 +01:00
}
2021-06-11 17:14:12 +01:00
ShutterGlobal . RelayShutterMask | = 3 < < ( Settings - > shutter_startrelay [ i ] - 1 ) ;
2019-09-30 10:21:43 +01:00
2021-06-11 17:14:12 +01:00
Shutter [ i ] . real_position = ShutterPercentToRealPosition ( Settings - > shutter_position [ i ] , i ) ;
2020-09-08 18:34:10 +01:00
2020-09-09 13:04:57 +01:00
Shutter [ i ] . start_position = Shutter [ i ] . target_position = Shutter [ i ] . real_position ;
2021-06-11 17:14:12 +01:00
Shutter [ i ] . motordelay = Settings - > shutter_motordelay [ i ] ;
Shutter [ i ] . lastdirection = ( 50 < Settings - > shutter_position [ i ] ) ? 1 : - 1 ;
2019-10-01 15:33:39 +01:00
2021-11-07 14:53:12 +00:00
// Venetian Blind
2022-12-22 16:02:01 +00:00
// ensure min is smaller than max
Settings - > shutter_tilt_config [ 2 ] [ i ] = Settings - > shutter_tilt_config [ 0 ] [ i ] > = Settings - > shutter_tilt_config [ 1 ] [ i ] ? 0 : Settings - > shutter_tilt_config [ 2 ] [ i ] ;
//copy config to shutter
2021-11-07 14:53:12 +00:00
for ( uint8_t k = 0 ; k < 5 ; k + + ) {
Shutter [ i ] . tilt_config [ k ] = Settings - > shutter_tilt_config [ k ] [ i ] ;
}
2022-12-22 16:02:01 +00:00
// wipe open/close position if duration is 0
if ( Shutter [ i ] . tilt_config [ 2 ] = = 0 ) {
Shutter [ i ] . tilt_config [ 3 ] = Shutter [ i ] . tilt_config [ 4 ] = 0 ;
}
2021-11-16 16:05:45 +00:00
Shutter [ i ] . tilt_target_pos = Shutter [ i ] . tilt_real_pos = Settings - > shutter_tilt_pos [ i ] ;
2021-11-15 18:52:48 +00:00
Shutter [ i ] . tilt_velocity = Shutter [ i ] . tilt_config [ 2 ] > 0 ? ( ( Shutter [ i ] . tilt_config [ 1 ] - Shutter [ i ] . tilt_config [ 0 ] ) / Shutter [ i ] . tilt_config [ 2 ] ) + 1 : 1 ;
2021-11-07 14:53:12 +00:00
2021-11-14 21:17:28 +00:00
Shutter [ i ] . close_velocity_max = ShutterGlobal . open_velocity_max * Shutter [ i ] . open_time / Shutter [ i ] . close_time ;
Shutter [ i ] . min_realPositionChange = 2 * tmax ( ShutterGlobal . open_velocity_max , Shutter [ i ] . close_velocity_max ) ;
Shutter [ i ] . min_TiltChange = 2 * Shutter [ i ] . tilt_velocity ;
2021-11-07 14:53:12 +00:00
2020-09-09 13:04:57 +01:00
switch ( ShutterGlobal . position_mode ) {
2020-09-05 19:39:24 +01:00
case SHT_PWM_VALUE :
2020-09-09 13:04:57 +01:00
ShutterGlobal . open_velocity_max = RESOLUTION ;
2020-09-09 14:24:21 +01:00
// Initiate pwm range with defaults if not already set.
2021-09-15 09:08:09 +01:00
Settings - > shutter_pwmrange [ 0 ] [ i ] = Settings - > shutter_pwmrange [ 0 ] [ i ] > 0 ? Settings - > shutter_pwmrange [ 0 ] [ i ] : pwm_servo_min ;
Settings - > shutter_pwmrange [ 1 ] [ i ] = Settings - > shutter_pwmrange [ 1 ] [ i ] > 0 ? Settings - > shutter_pwmrange [ 1 ] [ i ] : pwm_servo_max ;
2021-11-14 21:17:28 +00:00
Shutter [ i ] . min_realPositionChange = 0 ;
Shutter [ i ] . min_TiltChange = 0 ;
2020-09-05 19:39:24 +01:00
break ;
2022-12-14 10:25:41 +00:00
case SHT_TIME :
// Test is the relays are in interlock mode. Disable shuttermode if error
if ( ! relay_in_interlock ) {
TasmotaGlobal . shutters_present = 0 ,
2022-12-21 14:26:42 +00:00
AddLog ( LOG_LEVEL_ERROR , PSTR ( " SHT: ERROR: Shtr%d Relays are not in INTERLOCK. Pls read documentation. Shutter DISABLE. Fix and REBOOT " ) , i + 1 ) ;
2022-12-14 10:25:41 +00:00
return ;
2022-12-21 14:26:42 +00:00
}
2022-12-14 10:25:41 +00:00
break ;
2020-09-05 19:39:24 +01:00
}
2022-12-21 14:26:42 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " SHT: Shtr%d min realpos_chg: %d, min tilt_chg %d " ) , i + 1 , Shutter [ i ] . min_realPositionChange , Shutter [ i ] . min_TiltChange ) ;
2023-11-03 15:01:57 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " SHT: Shtr%d Openvel %d, Closevel: %d " ) , i + 1 , ShutterGlobal . open_velocity_max , Shutter [ i ] . close_velocity_max ) ;
2021-11-14 21:17:28 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " SHT: Shtr%d Init. Pos %d, Inv %d, Locked %d, Endstop enab %d, webButt inv %d, Motordel: %d " ) ,
2020-09-09 13:04:57 +01:00
i + 1 , Shutter [ i ] . real_position ,
2022-12-21 14:26:42 +00:00
( Settings - > shutter_options [ i ] & 1 ) ? 1 : 0 , ( Settings - > shutter_options [ i ] & 2 ) ? 1 : 0 , ( Settings - > shutter_options [ i ] & 4 ) ? 1 : 0 , ( Settings - > shutter_options [ i ] & 8 ) ? 1 : 0 , Shutter [ i ] . motordelay ) ;
2019-09-29 17:00:01 +01:00
} else {
2020-09-09 13:04:57 +01:00
// terminate loop at first INVALID Shutter[i].
2019-09-29 17:00:01 +01:00
break ;
}
2020-01-22 12:23:59 +00:00
ShutterLimitRealAndTargetPositions ( i ) ;
2021-06-11 17:14:12 +01:00
Settings - > shutter_accuracy = 1 ;
2022-12-14 10:25:41 +00:00
Settings - > shutter_mode = ShutterGlobal . position_mode ;
// initialize MotorStop time with 500ms if not set
2022-12-23 08:53:10 +00:00
// typical not set start values are 0 and 65535
if ( Settings - > shutter_motorstop > 5000 | | Settings - > shutter_motorstop = = 0 ) {
Settings - > shutter_motorstop = 500 ;
}
2019-09-29 17:00:01 +01:00
}
}
2020-04-21 15:19:42 +01:00
void ShutterReportPosition ( bool always , uint32_t index )
2020-01-04 01:36:05 +00:00
{
Response_P ( PSTR ( " { " ) ) ;
2020-04-21 15:19:42 +01:00
uint32_t i = 0 ;
2020-10-30 11:29:48 +00:00
uint32_t n = TasmotaGlobal . shutters_present ;
2021-11-16 20:21:04 +00:00
uint8_t shutter_running = 0 ;
2022-11-25 16:04:57 +00:00
for ( i ; i < n ; i + + ) {
if ( Shutter [ i ] . direction ! = 0 ) {
shutter_running + + ;
}
}
2022-12-21 14:26:42 +00:00
2022-11-25 16:04:57 +00:00
// Allow function exit if nothing to report (99.9% use case)
if ( ! always & & ! shutter_running ) return ;
2020-04-21 15:19:42 +01:00
if ( index ! = MAX_SHUTTERS ) {
i = index ;
n = index + 1 ;
2022-11-25 16:04:57 +00:00
} else {
i = 0 ;
2020-04-21 15:19:42 +01:00
}
for ( i ; i < n ; i + + ) {
2021-02-18 11:42:59 +00:00
//AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Shtr%d Real Pos %d"), i+1,Shutter[i].real_position);
2022-12-21 14:26:42 +00:00
2020-09-09 13:04:57 +01:00
if ( Shutter [ i ] . direction ! = 0 ) {
2020-01-04 14:09:57 +00:00
ShutterLogPos ( i ) ;
2021-11-16 20:21:04 +00:00
shutter_running + + ;
2020-01-04 01:36:05 +00:00
}
2020-04-21 15:19:42 +01:00
if ( i & & index = = MAX_SHUTTERS ) { ResponseAppend_P ( PSTR ( " , " ) ) ; }
2023-09-29 16:46:41 +01:00
uint8_t position = ShutterRealToPercentPosition ( Shutter [ i ] . real_position , i ) ;
uint8_t target = ShutterRealToPercentPosition ( Shutter [ i ] . target_position , i ) ;
ResponseAppend_P ( JSON_SHUTTER_POS , i + 1 , ( Settings - > shutter_options [ i ] & 1 ) ? 100 - position : position , Shutter [ i ] . direction , ( Settings - > shutter_options [ i ] & 1 ) ? 100 - target : target , Shutter [ i ] . tilt_real_pos ) ;
2020-01-04 01:36:05 +00:00
}
ResponseJsonEnd ( ) ;
2021-11-16 20:21:04 +00:00
if ( always | | shutter_running ) {
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_STAT , PSTR ( D_PRFX_SHUTTER ) ) ; // RulesProcess() now re-entry protected
2020-01-04 01:36:05 +00:00
}
2021-02-18 11:42:59 +00:00
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: rules_flag.shutter_moving: %d, moved %d"), TasmotaGlobal.rules_flag.shutter_moving, TasmotaGlobal.rules_flag.shutter_moved);
2020-01-04 01:36:05 +00:00
}
2021-11-14 21:17:28 +00:00
void ShutterLimitRealAndTargetPositions ( uint32_t i )
{
2020-09-09 13:04:57 +01:00
if ( Shutter [ i ] . real_position < 0 ) Shutter [ i ] . real_position = 0 ;
if ( Shutter [ i ] . real_position > Shutter [ i ] . open_max ) Shutter [ i ] . real_position = Shutter [ i ] . open_max ;
if ( Shutter [ i ] . target_position < 0 ) Shutter [ i ] . target_position = 0 ;
if ( Shutter [ i ] . target_position > Shutter [ i ] . open_max ) Shutter [ i ] . target_position = Shutter [ i ] . open_max ;
2020-01-13 11:00:34 +00:00
}
2020-09-05 19:39:24 +01:00
void ShutterCalculateAccelerator ( uint8_t i )
{
2020-09-09 07:58:00 +01:00
// No Logging allowed. Part of RTC Timer
2020-09-09 13:04:57 +01:00
if ( Shutter [ i ] . direction ! = 0 ) {
switch ( ShutterGlobal . position_mode ) {
2020-09-08 18:34:10 +01:00
case SHT_COUNTER :
case SHT_PWM_VALUE :
2021-02-22 15:54:58 +00:00
current_real_position = Shutter [ i ] . real_position ;
current_pwm_velocity = Shutter [ i ] . pwm_velocity ;
2020-09-08 18:34:10 +01:00
// calculate max velocity allowed in this direction
2020-09-09 13:04:57 +01:00
velocity_max = Shutter [ i ] . direction = = 1 ? ShutterGlobal . open_velocity_max : Shutter [ i ] . close_velocity_max ;
2020-09-08 18:34:10 +01:00
// calculate max change of velocyty based on the defined motordelay in steps
2020-09-09 13:04:57 +01:00
velocity_change_per_step_max = velocity_max / ( Shutter [ i ] . motordelay > 0 ? Shutter [ i ] . motordelay : 1 ) ;
2020-09-08 18:34:10 +01:00
// minimumtime required from current velocity to stop
2021-02-22 15:54:58 +00:00
min_runtime_ms = current_pwm_velocity * 1000 / STEPS_PER_SECOND / velocity_change_per_step_max ;
// decellaration way from current velocity
current_stop_way = min_runtime_ms * STEPS_PER_SECOND * ( current_pwm_velocity + velocity_change_per_step_max ) * Shutter [ i ] . direction / 2 / ShutterGlobal . open_velocity_max - ( Shutter [ i ] . accelerator < 0 ? Shutter [ i ] . direction * 1000 * current_pwm_velocity / ShutterGlobal . open_velocity_max : 0 ) ;
next_possible_stop_position = current_real_position + current_stop_way ;
2021-03-24 13:04:27 +00:00
// ensure that the accelerotor kicks in at the first overrun of the target position
if ( Shutter [ i ] . accelerator < 0 | | next_possible_stop_position * Shutter [ i ] . direction > Shutter [ i ] . target_position * Shutter [ i ] . direction ) {
// if startet to early because of 0.05sec maximum accuracy and final position is to far away (200) accelerate a bit less
if ( next_possible_stop_position * Shutter [ i ] . direction + 200 < Shutter [ i ] . target_position * Shutter [ i ] . direction ) {
Shutter [ i ] . accelerator = - velocity_change_per_step_max * 9 / 10 ;
} else {
// in any case increase accelleration if overrun is detected during decelleration
if ( next_possible_stop_position * Shutter [ i ] . direction > Shutter [ i ] . target_position * Shutter [ i ] . direction & & Shutter [ i ] . accelerator < 0 ) {
Shutter [ i ] . accelerator = - velocity_change_per_step_max * 11 / 10 ;
} else {
// as long as the calculated end position is ok stay with proposed decelleration
Shutter [ i ] . accelerator = - velocity_change_per_step_max ;
}
}
// detect during the acceleration phase the point final speed is reached
2021-02-22 15:54:58 +00:00
} else if ( Shutter [ i ] . accelerator > 0 & & current_pwm_velocity = = velocity_max ) {
2020-09-09 13:04:57 +01:00
Shutter [ i ] . accelerator = 0 ;
2020-09-08 18:34:10 +01:00
}
break ;
}
2020-09-05 19:39:24 +01:00
}
}
void ShutterDecellerateForStop ( uint8_t i )
{
2022-08-01 18:27:49 +01:00
# ifdef ESP32
bool pwm_apply = false ; // ESP32 only, do we need to apply PWM changes
# endif
2020-09-09 13:04:57 +01:00
switch ( ShutterGlobal . position_mode ) {
2020-09-05 19:39:24 +01:00
case SHT_PWM_VALUE :
case SHT_COUNTER :
int16_t missing_steps ;
2020-09-27 14:36:56 +01:00
Shutter [ i ] . accelerator = - ( ShutterGlobal . open_velocity_max / ( Shutter [ i ] . motordelay > 4 ? ( Shutter [ i ] . motordelay * 11 ) / 10 : 4 ) ) ;
2021-02-22 15:54:58 +00:00
2021-09-15 09:08:09 +01:00
while ( Shutter [ i ] . pwm_velocity > - 2 * Shutter [ i ] . accelerator & & Shutter [ i ] . pwm_velocity ! = PWM_MIN ) {
2021-02-22 15:54:58 +00:00
delay ( 50 ) ;
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: Velocity %ld, Delta %d " ) , Shutter [ i ] . pwm_velocity , Shutter [ i ] . accelerator ) ;
2020-09-05 19:39:24 +01:00
// Control will be done in RTC Ticker.
}
2020-09-09 13:04:57 +01:00
if ( ShutterGlobal . position_mode = = SHT_COUNTER ) {
missing_steps = ( ( Shutter [ i ] . target_position - Shutter [ i ] . start_position ) * Shutter [ i ] . direction * ShutterGlobal . open_velocity_max / RESOLUTION / STEPS_PER_SECOND ) - RtcSettings . pulse_counter [ i ] ;
2020-09-05 19:39:24 +01:00
//prepare for stop PWM
2020-09-09 13:04:57 +01:00
Shutter [ i ] . accelerator = 0 ;
Shutter [ i ] . pwm_velocity = 0 ;
2021-11-07 14:53:12 +00:00
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Remain %d count %d -> target %d, dir %d"), missing_steps, RtcSettings.pulse_counter[i], (uint32_t)(Shutter[i].target_position-Shutter[i].start_position)*Shutter[i].direction*ShutterGlobal.open_velocity_max/RESOLUTION/STEPS_PER_SECOND, Shutter[i].direction);
2021-09-05 21:07:50 +01:00
while ( RtcSettings . pulse_counter [ i ] < ( uint32_t ) ( Shutter [ i ] . target_position - Shutter [ i ] . start_position ) * Shutter [ i ] . direction * ShutterGlobal . open_velocity_max / RESOLUTION / STEPS_PER_SECOND & & missing_steps > 0 ) {
2020-09-05 19:39:24 +01:00
}
2022-07-22 13:48:08 +01:00
# ifdef ESP8266
2024-01-07 14:10:19 +00:00
AnalogWrite ( Pin ( GPIO_PWM1 , i ) , 0 ) ; // removed with 8.3 because of reset caused by watchog
2022-07-22 13:48:08 +01:00
# endif
# ifdef ESP32
TasmotaGlobal . pwm_value [ i ] = 0 ;
2022-08-01 18:27:49 +01:00
pwm_apply = true ;
2022-07-22 13:48:08 +01:00
# endif // ESP32
2020-09-09 13:04:57 +01:00
Shutter [ i ] . real_position = ShutterCalculatePosition ( i ) ;
2021-11-07 14:53:12 +00:00
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Remain steps %d"), missing_steps);
2021-02-22 15:54:58 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: Real %d, Pulsecount %d, tobe %d, Start %d " ) , Shutter [ i ] . real_position , RtcSettings . pulse_counter [ i ] , ( uint32_t ) ( Shutter [ i ] . target_position - Shutter [ i ] . start_position ) * Shutter [ i ] . direction * ShutterGlobal . open_velocity_max / RESOLUTION / STEPS_PER_SECOND , Shutter [ i ] . start_position ) ;
2020-09-05 19:39:24 +01:00
}
2020-09-09 13:04:57 +01:00
Shutter [ i ] . direction = 0 ;
Shutter [ i ] . pwm_velocity = 0 ;
2020-09-05 19:39:24 +01:00
break ;
}
2022-08-01 18:27:49 +01:00
# ifdef ESP32
if ( pwm_apply ) { PwmApplyGPIO ( false ) ; }
# endif
2020-09-05 19:39:24 +01:00
}
2021-11-14 21:17:28 +00:00
void ShutterPowerOff ( uint8_t i )
{
2022-02-04 07:58:42 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " SHT: Stop %d Mode %d time %d " ) , i + 1 , Shutter [ i ] . switch_mode , Shutter [ i ] . time ) ; // fix log to indicate correct shutter number
2020-09-05 19:39:24 +01:00
ShutterDecellerateForStop ( i ) ;
2022-01-14 09:11:52 +00:00
uint8_t cur_relay = Settings - > shutter_startrelay [ i ] + ( Shutter [ i ] . direction = = 1 ? 0 : ( uint8_t ) ( ShutterGlobal . position_mode = = SHT_TIME ) ) ;
2021-11-15 18:52:48 +00:00
if ( Shutter [ i ] . direction ! = 0 ) {
Shutter [ i ] . direction = 0 ;
}
2021-11-25 07:31:40 +00:00
if ( Shutter [ i ] . real_position = = Shutter [ i ] . start_position ) {
2021-11-26 08:05:01 +00:00
//AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Update target tilt shutter %d from %d to %d"), i+1, Shutter[i].tilt_target_pos , Shutter[i].tilt_real_pos);
2021-11-25 07:31:40 +00:00
Shutter [ i ] . tilt_target_pos = Shutter [ i ] . tilt_real_pos ;
}
2021-11-16 20:11:34 +00:00
TasmotaGlobal . rules_flag . shutter_moved = 1 ;
2020-09-09 13:04:57 +01:00
switch ( Shutter [ i ] . switch_mode ) {
2020-09-05 19:39:24 +01:00
case SHT_SWITCH :
2021-11-26 08:05:01 +00:00
for ( int8_t k = 0 ; k < 2 ; k + + ) {
if ( ( 1 < < ( Settings - > shutter_startrelay [ i ] + k - 1 ) ) & TasmotaGlobal . power ) {
ExecuteCommandPowerShutter ( Settings - > shutter_startrelay [ i ] + k , 0 , SRC_SHUTTER ) ;
}
2020-09-05 19:39:24 +01:00
}
break ;
case SHT_PULSE :
// we have a momentary switch here. Needs additional pulse on same relay after the end
2020-10-30 11:29:48 +00:00
if ( ( SRC_PULSETIMER = = TasmotaGlobal . last_source | | SRC_SHUTTER = = TasmotaGlobal . last_source | | SRC_WEBGUI = = TasmotaGlobal . last_source ) ) {
2020-09-05 19:39:24 +01:00
ExecuteCommandPowerShutter ( cur_relay , 1 , SRC_SHUTTER ) ;
// switch off direction relay to make it power less
2021-06-11 17:14:12 +01:00
if ( ( ( 1 < < ( Settings - > shutter_startrelay [ i ] ) ) & TasmotaGlobal . power ) & & Settings - > shutter_startrelay [ i ] + 1 ! = cur_relay ) {
ExecuteCommandPowerShutter ( Settings - > shutter_startrelay [ i ] + 1 , 0 , SRC_SHUTTER ) ;
2020-09-05 19:39:24 +01:00
}
} else {
2020-10-30 11:29:48 +00:00
TasmotaGlobal . last_source = SRC_SHUTTER ;
2020-09-05 19:39:24 +01:00
}
break ;
}
2020-09-08 18:34:10 +01:00
// Store current PWM value to ensure proper position after reboot.
2020-09-09 13:04:57 +01:00
switch ( ShutterGlobal . position_mode ) {
2020-09-08 18:34:10 +01:00
case SHT_PWM_VALUE :
2021-11-14 21:17:28 +00:00
Shutter [ i ] . pwm_value = SHT_DIV_ROUND ( ( Settings - > shutter_pwmrange [ 1 ] [ i ] - Settings - > shutter_pwmrange [ 0 ] [ i ] ) * Shutter [ i ] . target_position , Shutter [ i ] . open_max ) + Settings - > shutter_pwmrange [ 0 ] [ i ] ;
2024-01-07 14:10:19 +00:00
AnalogWrite ( Pin ( GPIO_PWM1 , i ) , Shutter [ i ] . pwm_value ) ;
2021-11-14 21:17:28 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " SHT: PWM final %d " ) , Shutter [ i ] . pwm_value ) ;
char scmnd [ 20 ] ;
# ifdef SHUTTER_CLEAR_PWM_ONSTOP
// free the PWM servo lock on stop.
2024-01-07 14:10:19 +00:00
AnalogWrite ( Pin ( GPIO_PWM1 , i ) , 0 ) ;
2021-11-14 21:17:28 +00:00
# endif
break ;
2020-09-08 18:34:10 +01:00
}
2022-07-18 14:39:26 +01:00
if ( Settings - > save_data ) {
TasmotaGlobal . save_data_counter = Settings - > save_data ;
}
2022-12-14 10:25:41 +00:00
Shutter [ i ] . last_stop_time = millis ( ) ;
}
2023-04-04 15:17:12 +01:00
void ShutterWaitForMotorStop ( uint8_t i )
2022-12-14 10:25:41 +00:00
{
2023-04-04 15:17:12 +01:00
Shutter [ i ] . last_stop_time = millis ( ) ;
ShutterWaitForMotorStart ( i ) ;
2022-12-14 10:25:41 +00:00
}
2023-04-04 15:17:12 +01:00
void ShutterWaitForMotorStart ( uint8_t i )
2022-12-14 10:25:41 +00:00
{
2024-08-15 13:28:10 +01:00
uint32_t start_time = Shutter [ i ] . last_stop_time ;
while ( TimePassedSince ( start_time ) < Settings - > shutter_motorstop & & TimePassedSince ( start_time ) > 0 ) {
2022-12-14 10:25:41 +00:00
loop ( ) ;
}
2022-12-21 14:26:42 +00:00
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Stoptime done"));
2020-09-05 19:39:24 +01:00
}
2019-09-30 10:21:43 +01:00
void ShutterUpdatePosition ( void )
2019-09-29 17:00:01 +01:00
{
char scommand [ CMDSZ ] ;
char stopic [ TOPSZ ] ;
2020-10-30 11:29:48 +00:00
for ( uint32_t i = 0 ; i < TasmotaGlobal . shutters_present ; i + + ) {
2020-09-09 13:04:57 +01:00
if ( Shutter [ i ] . direction ! = 0 ) {
if ( ! ShutterGlobal . start_reported ) {
2020-04-21 15:19:42 +01:00
ShutterReportPosition ( true , i ) ;
2020-09-09 13:04:57 +01:00
ShutterGlobal . start_reported = 1 ;
2020-04-21 15:18:18 +01:00
}
2021-11-16 12:02:36 +00:00
int32_t deltatime = Shutter [ i ] . time - Shutter [ i ] . last_reported_time ;
2021-11-17 10:28:27 +00:00
Shutter [ i ] . last_reported_time = Shutter [ i ] . time + 1 ;
2022-12-14 10:25:41 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: Shtr%d Time %d(%d), cStop %d, cVelo %d, mVelo %d, aVelo %d, mRun %d, aPos %d, aPos2 %d, nStop %d, Trgt %d, mVelo %d, Dir %d, Tilt %d, TrgtTilt: %d, Tiltmove: %d " ) ,
i + 1 , Shutter [ i ] . time , deltatime , current_stop_way , current_pwm_velocity , velocity_max , Shutter [ i ] . accelerator , min_runtime_ms , current_real_position , Shutter [ i ] . real_position ,
2021-11-17 10:28:27 +00:00
next_possible_stop_position , Shutter [ i ] . target_position , velocity_change_per_step_max , Shutter [ i ] . direction , Shutter [ i ] . tilt_real_pos , Shutter [ i ] . tilt_target_pos ,
Shutter [ i ] . tiltmoving ) ;
if ( ( ( Shutter [ i ] . real_position * Shutter [ i ] . direction > = Shutter [ i ] . target_position * Shutter [ i ] . direction & & Shutter [ i ] . tiltmoving = = 0 ) | |
( ( int16_t ) Shutter [ i ] . tilt_real_pos * Shutter [ i ] . direction * Shutter [ i ] . tilt_config [ 2 ] > = ( int16_t ) Shutter [ i ] . tilt_target_pos * Shutter [ i ] . direction * Shutter [ i ] . tilt_config [ 2 ] & & Shutter [ i ] . tiltmoving = = 1 ) )
| | ( ShutterGlobal . position_mode = = SHT_COUNTER & & Shutter [ i ] . accelerator < 0 & & Shutter [ i ] . pwm_velocity + Shutter [ i ] . accelerator < PWM_MIN ) ) {
if ( Shutter [ i ] . direction ! = 0 ) {
Shutter [ i ] . lastdirection = Shutter [ i ] . direction ;
}
ShutterPowerOff ( i ) ;
ShutterLimitRealAndTargetPositions ( i ) ;
Settings - > shutter_position [ i ] = ShutterRealToPercentPosition ( Shutter [ i ] . real_position , i ) ;
Shutter [ i ] . start_position = Shutter [ i ] . real_position ;
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Pre: Tilt not match %d -> %d, moving: %d"),Shutter[i].tilt_real_pos,Shutter[i].tilt_target_pos,Shutter[i].tiltmoving);
2022-11-25 16:04:57 +00:00
if ( abs ( Shutter [ i ] . tilt_real_pos - Shutter [ i ] . tilt_target_pos ) > Shutter [ i ] . min_TiltChange & & Shutter [ i ] . tiltmoving = = 0 ) {
2021-11-17 10:28:27 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " SHT: Tilt not match %d -> %d " ) , Shutter [ i ] . tilt_real_pos , Shutter [ i ] . tilt_target_pos ) ;
2021-11-24 08:34:51 +00:00
char databuf [ 1 ] = " " ;
XdrvMailbox . data = databuf ;
2021-11-17 10:28:27 +00:00
XdrvMailbox . payload = - 99 ;
XdrvMailbox . index = i + 1 ;
Shutter [ i ] . tiltmoving = 1 ;
CmndShutterPosition ( ) ;
return ;
} else {
Settings - > shutter_tilt_pos [ i ] = Shutter [ i ] . tilt_real_pos ;
}
ShutterLogPos ( i ) ;
2022-06-24 15:40:45 +01:00
if ( ! Settings - > flag4 . only_json_message ) { // SetOption90 - Disable non-json MQTT response
// sending MQTT result to broker
snprintf_P ( scommand , sizeof ( scommand ) , PSTR ( D_SHUTTER " %d " ) , i + 1 ) ;
GetTopic_P ( stopic , STAT , TasmotaGlobal . mqtt_topic , scommand ) ;
Response_P ( " %d " , ( Settings - > shutter_options [ i ] & 1 ) ? 100 - Settings - > shutter_position [ i ] : Settings - > shutter_position [ i ] ) ;
MqttPublish ( stopic , Settings - > flag . mqtt_power_retain ) ; // CMND_POWERRETAIN
}
2021-11-17 10:28:27 +00:00
ShutterReportPosition ( true , i ) ;
TasmotaGlobal . rules_flag . shutter_moved = 1 ;
2019-09-29 17:00:01 +01:00
}
}
}
}
2021-11-14 21:17:28 +00:00
bool ShutterState ( uint32_t device )
{
2021-04-03 13:14:01 +01:00
if ( device > 4 ) { return false ; }
2019-09-29 17:00:01 +01:00
device - - ;
device & = 3 ;
2021-06-11 17:14:12 +01:00
return ( Settings - > flag3 . shutter_mode & & // SetOption80 - Enable shutter support
( ShutterGlobal . RelayShutterMask & ( 1 < < ( Settings - > shutter_startrelay [ device ] - 1 ) ) ) ) ;
2019-09-29 17:00:01 +01:00
}
2022-06-24 15:40:45 +01:00
void ShutterAllowPreStartProcedure ( uint8_t i ) {
// Tricky!!! Execute command status 2 while in the 10 sec loop and you'll end up in an exception
// What PreStartProcedure do you want to execute here?
// Anyway, as long var1 != 99 this is skipped (luckily)
2020-10-11 09:51:20 +01:00
# ifdef USE_RULES
2024-08-15 09:08:54 +01:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: Delay Start? var%d <99>=<%s>, max10s? " ) , i + 1 , rules_vars [ i ] ) ;
// wait for response from rules
uint32_t start_time = millis ( ) ;
while ( TimePassedSince ( start_time ) < 10000 & & ( String ) rules_vars [ i ] = = " 99 " ) {
delay ( 1 ) ;
Insert hook to rule BEFORE moving
New hook that can guarantee a rule will be executed before the movement starts. Will change documentation:
change "var<shutternumber>" to 99 to enable hook. Create a rule that executes on shutter#moving ONCE and set VARx to 0. Add rule at shutter#moved=1 to set VARx=99 and enable ONCE again.
example:
{"Rule1":"ON","Once":"ON","StopOnError":"OFF","Length":62,"Free":449,"Rules":"on shutter#moving=1 do backlog power3 on;delay 10;var1 0 endon"}
{"Rule2":"ON","Once":"OFF","StopOnError":"OFF","Length":51,"Free":460,"Rules":"on shutter#moved=1 do backlog var1 99;rule1 5 endon"}
2020-10-08 18:55:17 +01:00
}
2020-10-11 09:51:20 +01:00
# endif // USE_RULES
Insert hook to rule BEFORE moving
New hook that can guarantee a rule will be executed before the movement starts. Will change documentation:
change "var<shutternumber>" to 99 to enable hook. Create a rule that executes on shutter#moving ONCE and set VARx to 0. Add rule at shutter#moved=1 to set VARx=99 and enable ONCE again.
example:
{"Rule1":"ON","Once":"ON","StopOnError":"OFF","Length":62,"Free":449,"Rules":"on shutter#moving=1 do backlog power3 on;delay 10;var1 0 endon"}
{"Rule2":"ON","Once":"OFF","StopOnError":"OFF","Length":51,"Free":460,"Rules":"on shutter#moved=1 do backlog var1 99;rule1 5 endon"}
2020-10-08 18:55:17 +01:00
}
2020-10-09 08:58:33 +01:00
2020-01-04 14:09:57 +00:00
void ShutterStartInit ( uint32_t i , int32_t direction , int32_t target_pos )
2019-09-29 17:00:01 +01:00
{
2021-11-16 12:46:22 +00:00
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: dir %d, delta1 %d, delta2 %d"),direction, (Shutter[i].open_max - Shutter[i].real_position) / Shutter[i].close_velocity, Shutter[i].real_position / Shutter[i].close_velocity);
2021-11-14 21:17:28 +00:00
if ( ( ( ( 1 = = direction ) & & ( ( Shutter [ i ] . open_max - Shutter [ i ] . real_position ) < = Shutter [ i ] . min_realPositionChange ) )
| | ( ( - 1 = = direction ) & & ( Shutter [ i ] . real_position < = Shutter [ i ] . min_realPositionChange ) ) )
& & abs ( Shutter [ i ] . tilt_real_pos - Shutter [ i ] . tilt_target_pos ) < = Shutter [ i ] . min_TiltChange ) {
2020-09-09 13:04:57 +01:00
ShutterGlobal . skip_relay_change = 1 ;
2019-12-12 15:14:06 +00:00
} else {
2020-09-09 13:04:57 +01:00
Shutter [ i ] . pwm_velocity = 0 ;
2023-04-04 15:17:12 +01:00
ShutterWaitForMotorStart ( i ) ;
2020-09-09 13:04:57 +01:00
switch ( ShutterGlobal . position_mode ) {
2020-09-05 19:39:24 +01:00
# ifdef SHUTTER_STEPPER
case SHT_COUNTER :
2022-07-22 13:48:08 +01:00
# ifdef ESP8266
2020-09-09 13:04:57 +01:00
analogWriteFreq ( Shutter [ i ] . pwm_velocity ) ;
2024-01-07 14:10:19 +00:00
AnalogWrite ( Pin ( GPIO_PWM1 , i ) , 0 ) ;
2022-07-22 13:48:08 +01:00
# endif
# ifdef ESP32
analogWriteFreq ( PWM_MIN , Pin ( GPIO_PWM1 , i ) ) ;
TasmotaGlobal . pwm_value [ i ] = 0 ;
PwmApplyGPIO ( false ) ;
# endif
2020-09-05 19:39:24 +01:00
RtcSettings . pulse_counter [ i ] = 0 ;
break ;
# endif
2019-12-04 11:34:27 +00:00
}
2021-11-16 12:46:22 +00:00
2020-09-09 13:04:57 +01:00
Shutter [ i ] . accelerator = ShutterGlobal . open_velocity_max / ( Shutter [ i ] . motordelay > 0 ? Shutter [ i ] . motordelay : 1 ) ;
Shutter [ i ] . target_position = target_pos ;
Shutter [ i ] . start_position = Shutter [ i ] . real_position ;
2020-10-29 12:58:50 +00:00
TasmotaGlobal . rules_flag . shutter_moving = 1 ;
Insert hook to rule BEFORE moving
New hook that can guarantee a rule will be executed before the movement starts. Will change documentation:
change "var<shutternumber>" to 99 to enable hook. Create a rule that executes on shutter#moving ONCE and set VARx to 0. Add rule at shutter#moved=1 to set VARx=99 and enable ONCE again.
example:
{"Rule1":"ON","Once":"ON","StopOnError":"OFF","Length":62,"Free":449,"Rules":"on shutter#moving=1 do backlog power3 on;delay 10;var1 0 endon"}
{"Rule2":"ON","Once":"OFF","StopOnError":"OFF","Length":51,"Free":460,"Rules":"on shutter#moved=1 do backlog var1 99;rule1 5 endon"}
2020-10-08 18:55:17 +01:00
ShutterAllowPreStartProcedure ( i ) ;
2021-11-16 13:00:49 +00:00
Shutter [ i ] . time = Shutter [ i ] . last_reported_time = 0 ;
2021-11-16 12:46:22 +00:00
Insert hook to rule BEFORE moving
New hook that can guarantee a rule will be executed before the movement starts. Will change documentation:
change "var<shutternumber>" to 99 to enable hook. Create a rule that executes on shutter#moving ONCE and set VARx to 0. Add rule at shutter#moved=1 to set VARx=99 and enable ONCE again.
example:
{"Rule1":"ON","Once":"ON","StopOnError":"OFF","Length":62,"Free":449,"Rules":"on shutter#moving=1 do backlog power3 on;delay 10;var1 0 endon"}
{"Rule2":"ON","Once":"OFF","StopOnError":"OFF","Length":51,"Free":460,"Rules":"on shutter#moved=1 do backlog var1 99;rule1 5 endon"}
2020-10-08 18:55:17 +01:00
ShutterGlobal . skip_relay_change = 0 ;
2020-10-29 12:58:50 +00:00
TasmotaGlobal . rules_flag . shutter_moved = 0 ;
2020-09-09 13:04:57 +01:00
ShutterGlobal . start_reported = 0 ;
2021-11-07 14:53:12 +00:00
Shutter [ i ] . tilt_real_pos = tmax ( tmin ( Shutter [ i ] . tilt_real_pos , Shutter [ i ] . tilt_config [ 1 ] ) , Shutter [ i ] . tilt_config [ 0 ] ) ;
Shutter [ i ] . tilt_start_pos = Shutter [ i ] . tilt_real_pos ;
2022-12-10 22:52:53 +00:00
if ( Shutter [ i ] . tilt_config [ 1 ] - Shutter [ i ] . tilt_config [ 0 ] ! = 0 ) {
2022-12-04 13:41:38 +00:00
Shutter [ i ] . venetian_delay = SHT_DIV_ROUND ( ( direction > 0 ? Shutter [ i ] . tilt_config [ 1 ] - Shutter [ i ] . tilt_real_pos : Shutter [ i ] . tilt_real_pos - Shutter [ i ] . tilt_config [ 0 ] ) * Shutter [ i ] . tilt_config [ 2 ] , Shutter [ i ] . tilt_config [ 1 ] - Shutter [ i ] . tilt_config [ 0 ] ) ;
2021-11-16 14:28:14 +00:00
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: real %d, start %d, counter %d,freq_max %d, dir %d, freq %d"),Shutter[i].real_position, Shutter[i].start_position ,RtcSettings.pulse_counter[i],ShutterGlobal.open_velocity_max , direction ,ShutterGlobal.open_velocity_max );
2021-11-16 12:46:22 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: VenetianDelay: %d, Pos: %d, Dir: %d, Delta: %d, Dur: %d, StartP: %d, TgtP: %d " ) ,
2021-11-16 14:28:14 +00:00
Shutter [ i ] . venetian_delay , Shutter [ i ] . tilt_real_pos , direction , ( Shutter [ i ] . tilt_config [ 1 ] - Shutter [ i ] . tilt_config [ 0 ] ) , Shutter [ i ] . tilt_config [ 2 ] , Shutter [ i ] . tilt_start_pos , Shutter [ i ] . tilt_target_pos ) ;
2021-11-07 16:20:20 +00:00
}
2022-07-18 14:39:26 +01:00
// avoid file system writes during move to minimize missing steps
if ( Settings - > save_data ) {
uint32_t move_duration = ( direction > 0 ) ? Shutter [ i ] . open_time : Shutter [ i ] . close_time ;
TasmotaGlobal . save_data_counter = Settings - > save_data + ( move_duration / 10 ) + 1 ;
}
2019-12-02 20:44:05 +00:00
}
2021-11-16 12:46:22 +00:00
//AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Start shtr%d from %d to %d in dir: %d"), i, Shutter[i].start_position, Shutter[i].target_position, direction);
2022-12-21 14:26:42 +00:00
2021-11-16 12:46:22 +00:00
Shutter [ i ] . direction = direction ; // Last action. This causes RTC to start.
2019-09-29 17:00:01 +01:00
}
2020-09-05 19:39:24 +01:00
int32_t ShutterCalculatePosition ( uint32_t i )
2019-12-15 13:21:41 +00:00
{
2020-11-14 13:02:15 +00:00
// No Logging allowed. Part of RTC Timer
2020-09-09 13:04:57 +01:00
if ( Shutter [ i ] . direction ! = 0 ) {
switch ( ShutterGlobal . position_mode ) {
2020-09-08 18:34:10 +01:00
case SHT_COUNTER :
2021-02-18 11:42:59 +00:00
return ( ( int64_t ) RtcSettings . pulse_counter [ i ] * Shutter [ i ] . direction * STEPS_PER_SECOND * RESOLUTION / ShutterGlobal . open_velocity_max ) + Shutter [ i ] . start_position ;
2020-09-08 18:34:10 +01:00
break ;
case SHT_TIME :
case SHT_TIME_UP_DOWN :
case SHT_TIME_GARAGE :
2021-11-13 16:31:04 +00:00
if ( Shutter [ i ] . tilt_config [ 2 ] > 0 ) {
2023-04-04 16:45:33 +01:00
if ( Shutter [ i ] . time < = Shutter [ i ] . venetian_delay + Shutter [ i ] . motordelay ) {
Shutter [ i ] . tilt_real_pos = ( Shutter [ i ] . tilt_start_pos + ( ( Shutter [ i ] . direction * ( int16_t ) ( Shutter [ i ] . time - tmin ( Shutter [ i ] . motordelay , Shutter [ i ] . time ) ) * ( Shutter [ i ] . tilt_config [ 1 ] - Shutter [ i ] . tilt_config [ 0 ] ) ) / Shutter [ i ] . tilt_config [ 2 ] ) ) ;
2021-11-13 16:31:04 +00:00
} else {
Shutter [ i ] . tilt_real_pos = Shutter [ i ] . direction = = 1 ? Shutter [ i ] . tilt_config [ 1 ] : Shutter [ i ] . tilt_config [ 0 ] ;
2021-11-14 21:17:28 +00:00
}
2021-11-07 14:53:12 +00:00
}
return Shutter [ i ] . start_position + ( ( Shutter [ i ] . time - tmin ( Shutter [ i ] . venetian_delay + Shutter [ i ] . motordelay , Shutter [ i ] . time ) ) * ( Shutter [ i ] . direction > 0 ? RESOLUTION : - Shutter [ i ] . close_velocity ) ) ;
2020-09-08 18:34:10 +01:00
break ;
case SHT_PWM_TIME :
break ;
case SHT_PWM_VALUE :
2020-09-09 13:04:57 +01:00
return Shutter [ i ] . real_position ;
2020-09-05 19:39:24 +01:00
break ;
2020-09-08 18:34:10 +01:00
default :
break ;
2020-09-05 19:39:24 +01:00
}
2020-11-14 13:02:15 +00:00
} else {
return Shutter [ i ] . real_position ;
}
return 0 ; // Never reaches here, Satisfy compiler
2019-12-15 13:21:41 +00:00
}
2019-09-30 10:21:43 +01:00
void ShutterRelayChanged ( void )
2019-09-29 17:00:01 +01:00
{
2020-09-09 13:04:57 +01:00
// ShutterGlobal.RelayCurrentMask = binary relay that was recently changed and cause an Action
2019-09-29 17:00:01 +01:00
// powerstate_local = binary powermatrix and relays from shutter: 0..3
// relays_changed = bool if one of the relays that belong to the shutter changed not by shutter or pulsetimer
char stemp1 [ 10 ] ;
2020-10-30 11:29:48 +00:00
for ( uint32_t i = 0 ; i < TasmotaGlobal . shutters_present ; i + + ) {
2021-06-11 17:14:12 +01:00
power_t powerstate_local = ( TasmotaGlobal . power > > ( Settings - > shutter_startrelay [ i ] - 1 ) ) & 3 ;
2020-09-05 19:39:24 +01:00
// SRC_IGNORE added because INTERLOCK function bite causes this as last source for changing the relay.
2021-06-11 17:14:12 +01:00
//uint8 manual_relays_changed = ((ShutterGlobal.RelayCurrentMask >> (Settings->shutter_startrelay[i] -1)) & 3) && SRC_IGNORE != TasmotaGlobal.last_source && SRC_SHUTTER != TasmotaGlobal.last_source && SRC_PULSETIMER != TasmotaGlobal.last_source ;
uint8 manual_relays_changed = ( ( ShutterGlobal . RelayCurrentMask > > ( Settings - > shutter_startrelay [ i ] - 1 ) ) & 3 ) & & SRC_SHUTTER ! = TasmotaGlobal . last_source & & SRC_PULSETIMER ! = TasmotaGlobal . last_source ;
2021-11-15 18:52:48 +00:00
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shtr%d, Source %s, Powerstate %ld, RelayMask %d, ManualChange %d"),
2021-11-15 18:55:04 +00:00
// i+1, GetTextIndexed(stemp1, sizeof(stemp1), TasmotaGlobal.last_source, kCommandSource), powerstate_local,ShutterGlobal.RelayCurrentMask,manual_relays_changed);
2019-09-29 17:00:01 +01:00
if ( manual_relays_changed ) {
2020-09-09 13:04:57 +01:00
//ShutterGlobal.skip_relay_change = true;
2020-01-13 11:00:34 +00:00
ShutterLimitRealAndTargetPositions ( i ) ;
2020-09-09 13:04:57 +01:00
switch ( Shutter [ i ] . switch_mode ) {
2020-09-05 19:39:24 +01:00
case SHT_PULSE :
2020-09-09 13:04:57 +01:00
if ( Shutter [ i ] . direction ! = 0 & & powerstate_local ) {
Shutter [ i ] . target_position = Shutter [ i ] . real_position ;
2020-09-05 19:39:24 +01:00
powerstate_local = 0 ;
2021-02-18 11:42:59 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: Shtr%d, Switch OFF motor. Target %ld, Source %s, Powerstate %ld, RelayMask %d, ManualChange %d " ) ,
2021-01-02 14:47:03 +00:00
i + 1 , Shutter [ i ] . target_position , GetTextIndexed ( stemp1 , sizeof ( stemp1 ) , TasmotaGlobal . last_source , kCommandSource ) , powerstate_local , ShutterGlobal . RelayCurrentMask , manual_relays_changed ) ;
2020-09-05 19:39:24 +01:00
}
break ;
default :
2020-10-30 11:29:48 +00:00
TasmotaGlobal . last_source = SRC_SHUTTER ; // avoid switch off in the next loop
2020-12-15 11:40:47 +00:00
if ( Shutter [ i ] . direction ! = 0 ) Shutter [ i ] . target_position = Shutter [ i ] . real_position ;
2020-09-05 19:39:24 +01:00
}
2021-11-24 16:43:25 +00:00
if ( powerstate_local > 0 ) {
Shutter [ i ] . tiltmoving = 0 ;
}
2020-09-09 13:04:57 +01:00
switch ( ShutterGlobal . position_mode ) {
2023-05-21 11:25:11 +01:00
// enum Shutterposition_mode {SHT_TIME, SHT_TIME_UP_DOWN, SHT_TIME_GARAGE, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME,};
case SHT_TIME_UP_DOWN :
case SHT_COUNTER :
case SHT_PWM_VALUE :
case SHT_PWM_TIME :
2023-06-14 15:14:10 +01:00
ShutterPowerOff ( i ) ;
2023-05-21 11:25:11 +01:00
case SHT_TIME : {
// powerstate_local == 0 => direction=0, stop
// powerstate_local == 1 => direction=1, target=Shutter[i].open_max
// powerstate_local == 2 => direction=-1, target=0 // only happen on SHT_TIME
// powerstate_local == 3 => direction=-1, target=0 // only happen if NOT SHT_TIME
int8_t direction = ( powerstate_local = = 0 ) ? 0 : ( powerstate_local = = 1 ) ? 1 : - 1 ;
2023-06-14 15:14:10 +01:00
int32_t target = ( powerstate_local = = 1 ) ? Shutter [ i ] . open_max : 0 ;
2023-05-21 11:25:11 +01:00
if ( direction ! = 0 ) {
ShutterStartInit ( i , direction , target ) ;
} else {
2020-09-09 13:04:57 +01:00
Shutter [ i ] . target_position = Shutter [ i ] . real_position ;
2023-04-04 15:17:12 +01:00
Shutter [ i ] . last_stop_time = millis ( ) ;
2020-09-05 19:39:24 +01:00
}
2023-05-21 11:25:11 +01:00
break ;
}
case SHT_TIME_GARAGE :
switch ( powerstate_local ) {
case 1 :
ShutterStartInit ( i , Shutter [ i ] . lastdirection * - 1 , Shutter [ i ] . lastdirection = = 1 ? 0 : Shutter [ i ] . open_max ) ;
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: Shtr%d Garage. NewTarget %d " ) , i , Shutter [ i ] . target_position ) ;
break ;
2020-09-05 19:39:24 +01:00
default :
2020-09-09 13:04:57 +01:00
Shutter [ i ] . target_position = Shutter [ i ] . real_position ;
2023-05-21 11:25:11 +01:00
}
}
2021-11-24 16:43:25 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: Shtr%d, Target %ld, Power: %d, tiltmv: %d " ) , i + 1 , Shutter [ i ] . target_position , powerstate_local , Shutter [ i ] . tiltmoving ) ;
2020-09-05 19:39:24 +01:00
} // if (manual_relays_changed)
2020-10-30 11:29:48 +00:00
} // for (uint32_t i = 0; i < TasmotaGlobal.shutters_present; i++)
2019-09-29 17:00:01 +01:00
}
2021-11-14 21:17:28 +00:00
bool ShutterButtonIsSimultaneousHold ( uint32_t button_index , uint32_t shutter_index )
{
2020-01-09 13:48:23 +00:00
// check for simultaneous shutter button hold
2020-03-02 19:32:55 +00:00
uint32 min_shutterbutton_hold_timer = - 1 ; // -1 == max(uint32)
2021-02-05 11:27:59 +00:00
for ( uint32_t i = 0 ; i < MAX_SHUTTER_KEYS ; i + + ) {
2021-06-11 17:14:12 +01:00
if ( ( button_index ! = i ) & & ( Settings - > shutter_button [ i ] & ( 1 < < 31 ) ) & & ( ( Settings - > shutter_button [ i ] & 0x03 ) = = shutter_index ) & & ( Button . hold_timer [ i ] < min_shutterbutton_hold_timer ) )
2020-01-09 13:48:23 +00:00
min_shutterbutton_hold_timer = Button . hold_timer [ i ] ;
}
2020-03-02 19:32:55 +00:00
return ( ( - 1 ! = min_shutterbutton_hold_timer ) & & ( min_shutterbutton_hold_timer > ( Button . hold_timer [ button_index ] > > 1 ) ) ) ;
2020-01-09 13:48:23 +00:00
}
2023-03-29 14:43:19 +01:00
bool ShutterButtonHandler ( void )
2020-01-02 10:23:11 +00:00
{
2023-05-21 11:25:11 +01:00
uint8_t buttonState = SHT_NOT_PRESSED ;
uint8_t button = XdrvMailbox . payload ;
uint8_t press_index ;
2020-01-02 10:23:11 +00:00
uint32_t button_index = XdrvMailbox . index ;
2023-05-21 11:25:11 +01:00
uint8_t shutter_index = Settings - > shutter_button [ button_index ] & 0x03 ;
2021-06-11 17:14:12 +01:00
uint16_t loops_per_second = 1000 / Settings - > button_debounce ; // ButtonDebounce (50)
2020-03-02 19:32:55 +00:00
2020-01-02 10:23:11 +00:00
if ( ( PRESSED = = button ) & & ( NOT_PRESSED = = Button . last_state [ button_index ] ) ) {
2021-06-11 17:14:12 +01:00
if ( Settings - > flag . button_single ) { // SetOption13 (0) - Allow only single button press for immediate action
2020-01-02 10:23:11 +00:00
buttonState = SHT_PRESSED_MULTI ;
press_index = 1 ;
} else {
2020-09-09 13:04:57 +01:00
if ( ( Shutter [ shutter_index ] . direction ) & & ( Button . press_counter [ button_index ] = = 0 ) ) {
2020-01-02 10:23:11 +00:00
buttonState = SHT_PRESSED_IMMEDIATE ;
press_index = 1 ;
Button . press_counter [ button_index ] = 99 ; // Remember to discard further action for press & hold within button timings
2020-02-24 11:23:03 +00:00
} else {
2020-01-02 10:23:11 +00:00
Button . press_counter [ button_index ] = ( Button . window_timer [ button_index ] ) ? Button . press_counter [ button_index ] + 1 : 1 ;
2020-03-02 19:49:11 +00:00
// Button.window_timer[button_index] = (Button.press_counter[button_index]==1) ? loops_per_second / 2 : loops_per_second; // 0.5 second multi press window after 1st press, 1s afterwards
2020-03-10 07:41:37 +00:00
Button . window_timer [ button_index ] = ( loops_per_second > > 2 ) * 3 ; // 0.75 second multi press window
2020-02-24 11:23:03 +00:00
}
2020-01-02 10:23:11 +00:00
}
2020-10-29 11:39:44 +00:00
TasmotaGlobal . blinks = 201 ;
2020-01-02 10:23:11 +00:00
}
if ( NOT_PRESSED = = button ) {
2022-12-10 22:52:53 +00:00
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shtr%d, Button %d, hold %d, dir %d, index %d, payload %d"), shutter_index+1, button_index+1, Button.hold_timer[button_index],Shutter[shutter_index].direction,XdrvMailbox.index,XdrvMailbox.payload);
2023-08-05 13:51:56 +01:00
if ( Shutter [ shutter_index ] . direction
& & ( Button . hold_timer [ button_index ] > 0
& & ( ! Settings - > flag . button_single
| | Button . hold_timer [ button_index ] > 20 ) )
& & ! ( Settings - > shutter_button [ button_index ] & ( 0x01 < < 29 ) ) ) {
2022-08-15 20:55:57 +01:00
XdrvMailbox . index = shutter_index + 1 ;
XdrvMailbox . payload = XdrvMailbox . index ;
2022-08-15 20:57:56 +01:00
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shtr%d, Button %d, hold %d, dir %d, index %d, payload %d"), shutter_index+1, button_index+1, Button.hold_timer[button_index],Shutter[shutter_index].direction,XdrvMailbox.index,XdrvMailbox.payload);
2022-08-15 20:55:57 +01:00
CmndShutterStop ( ) ;
}
2020-01-02 10:23:11 +00:00
Button . hold_timer [ button_index ] = 0 ;
} else {
Button . hold_timer [ button_index ] + + ;
2023-05-21 11:25:11 +01:00
if ( ! Settings - > flag . button_single ) { // SetOption13 (0) - Allow only single button press for immediate action
if ( Settings - > param [ P_HOLD_IGNORE ] > 0 ) { // SetOption40 (0) - Do not ignore button hold
2021-06-11 17:14:12 +01:00
if ( Button . hold_timer [ button_index ] > loops_per_second * Settings - > param [ P_HOLD_IGNORE ] / 10 ) {
2020-01-02 10:23:11 +00:00
Button . hold_timer [ button_index ] = 0 ; // Reset button hold counter to stay below hold trigger
Button . press_counter [ button_index ] = 0 ; // Discard button press to disable functionality
}
}
2021-06-11 17:14:12 +01:00
if ( ( Button . press_counter [ button_index ] < 99 ) & & ( Button . hold_timer [ button_index ] = = loops_per_second * Settings - > param [ P_HOLD_TIME ] / 10 ) ) { // press still valid && SetOption32 (40) - Button hold
2020-01-22 12:23:59 +00:00
// check for simultaneous shutter button hold
2020-01-30 13:33:33 +00:00
if ( ShutterButtonIsSimultaneousHold ( button_index , shutter_index ) ) {
2020-01-22 12:23:59 +00:00
// simultaneous shutter button hold detected
2021-02-05 11:27:59 +00:00
for ( uint32_t i = 0 ; i < MAX_SHUTTER_KEYS ; i + + )
2021-06-11 17:14:12 +01:00
if ( ( Settings - > shutter_button [ i ] & ( 1 < < 31 ) ) & & ( ( Settings - > shutter_button [ i ] & 0x03 ) = = shutter_index ) )
2020-01-22 12:23:59 +00:00
Button . press_counter [ i ] = 99 ; // Remember to discard further action for press & hold within button timings
press_index = 0 ;
buttonState = SHT_PRESSED_HOLD_SIMULTANEOUS ;
2020-01-09 13:48:23 +00:00
}
2020-01-22 12:23:59 +00:00
if ( Button . press_counter [ button_index ] < 99 ) {
press_index = 0 ;
2020-01-02 10:23:11 +00:00
buttonState = SHT_PRESSED_HOLD ;
2020-01-22 12:23:59 +00:00
}
2020-01-02 10:23:11 +00:00
Button . press_counter [ button_index ] = 0 ;
}
2021-06-11 17:14:12 +01:00
if ( ( Button . press_counter [ button_index ] = = 0 ) & & ( Button . hold_timer [ button_index ] = = loops_per_second * IMMINENT_RESET_FACTOR * Settings - > param [ P_HOLD_TIME ] / 10 ) ) { // SetOption32 (40) - Button held for factor times longer
2020-03-02 19:32:55 +00:00
press_index = - 1 ;
2020-01-09 13:48:23 +00:00
// check for simultaneous shutter button extend hold
2020-01-30 13:33:33 +00:00
if ( ShutterButtonIsSimultaneousHold ( button_index , shutter_index ) ) {
2020-01-09 13:48:23 +00:00
// simultaneous shutter button extend hold detected
buttonState = SHT_PRESSED_EXT_HOLD_SIMULTANEOUS ;
2020-03-02 19:32:55 +00:00
} else {
buttonState = SHT_PRESSED_EXT_HOLD ;
2020-01-09 13:48:23 +00:00
}
}
2020-01-02 10:23:11 +00:00
}
}
2021-06-11 17:14:12 +01:00
if ( ! Settings - > flag . button_single ) { // SetOption13 (0) - Allow multi-press
2020-01-02 10:23:11 +00:00
if ( Button . window_timer [ button_index ] ) {
Button . window_timer [ button_index ] - - ;
} else {
2020-10-29 11:21:24 +00:00
if ( ! TasmotaGlobal . restart_flag & & ! Button . hold_timer [ button_index ] & & ( Button . press_counter [ button_index ] > 0 ) ) {
2020-01-02 10:23:11 +00:00
if ( Button . press_counter [ button_index ] < 99 ) {
2020-01-22 12:23:59 +00:00
// check for simultaneous shutter button press
2020-03-02 19:32:55 +00:00
uint32 min_shutterbutton_press_counter = - 1 ; // -1 == max(uint32)
2021-02-05 11:27:59 +00:00
for ( uint32_t i = 0 ; i < MAX_SHUTTER_KEYS ; i + + ) {
2021-02-18 11:42:59 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: ShutterButton[i] %ld, ShutterIndex %d, ButtonPressCounter[i] %d, minShutterButtonPressCounter %d, i %d " ) ,
2021-06-11 17:14:12 +01:00
Settings - > shutter_button [ i ] , shutter_index , Button . press_counter [ i ] , min_shutterbutton_press_counter , i ) ;
if ( ( button_index ! = i ) & & ( Settings - > shutter_button [ i ] & ( 1 < < 31 ) ) & & ( ( Settings - > shutter_button [ i ] & 0x03 ) = = shutter_index ) & & ( i ! = button_index ) & & ( Button . press_counter [ i ] < min_shutterbutton_press_counter ) ) {
2020-01-22 12:23:59 +00:00
min_shutterbutton_press_counter = Button . press_counter [ i ] ;
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: minShutterButtonPressCounter %d " ) , min_shutterbutton_press_counter ) ;
2020-02-24 11:23:03 +00:00
}
2020-01-22 12:23:59 +00:00
}
if ( min_shutterbutton_press_counter = = Button . press_counter [ button_index ] ) {
// simultaneous shutter button press detected
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: Simultanous press detected " ) ) ;
2020-01-22 12:23:59 +00:00
press_index = Button . press_counter [ button_index ] ;
2021-02-05 11:27:59 +00:00
for ( uint32_t i = 0 ; i < MAX_SHUTTER_KEYS ; i + + )
2021-06-11 17:14:12 +01:00
if ( ( Settings - > shutter_button [ i ] & ( 1 < < 31 ) ) & & ( ( Settings - > shutter_button [ i ] & 0x03 ) ! = shutter_index ) )
2020-01-22 12:23:59 +00:00
Button . press_counter [ i ] = 99 ; // Remember to discard further action for press & hold within button timings
buttonState = SHT_PRESSED_MULTI_SIMULTANEOUS ;
2020-01-09 13:48:23 +00:00
}
2020-01-22 12:23:59 +00:00
if ( ( buttonState ! = SHT_PRESSED_MULTI_SIMULTANEOUS ) & & ( Button . press_counter [ button_index ] < 99 ) ) {
2020-01-09 13:48:23 +00:00
// no simultaneous shutter button press >3 detected
2020-01-22 12:23:59 +00:00
press_index = Button . press_counter [ button_index ] ;
2020-01-09 13:48:23 +00:00
buttonState = SHT_PRESSED_MULTI ;
}
2020-01-02 10:23:11 +00:00
}
Button . press_counter [ button_index ] = 0 ;
}
}
}
2020-03-02 19:32:55 +00:00
if ( buttonState ! = SHT_NOT_PRESSED ) {
2021-06-11 17:14:12 +01:00
if ( ( ! Settings - > flag . button_restrict ) & & ( ( ( press_index > = 5 ) & & ( press_index < = 7 ) ) | | ( buttonState = = SHT_PRESSED_EXT_HOLD ) | | ( buttonState = = SHT_PRESSED_EXT_HOLD_SIMULTANEOUS ) ) ) {
2020-03-02 19:32:55 +00:00
// check number of buttons for this shutter
uint8_t shutter_index_num_buttons = 0 ;
2021-02-05 11:27:59 +00:00
for ( uint32_t i = 0 ; i < MAX_SHUTTER_KEYS ; i + + ) {
2021-06-11 17:14:12 +01:00
if ( ( Settings - > shutter_button [ i ] & ( 1 < < 31 ) ) & & ( ( Settings - > shutter_button [ i ] & 0x03 ) = = shutter_index ) ) {
2020-03-02 19:32:55 +00:00
shutter_index_num_buttons + + ;
}
}
if ( ( buttonState = = SHT_PRESSED_MULTI_SIMULTANEOUS ) | | ( ( shutter_index_num_buttons = = 1 ) & & ( buttonState = = SHT_PRESSED_MULTI ) ) ) {
// 5x..7x && no SetOption1 (0) checked above
// simultaneous or stand alone button press 5x, 6x, 7x detected
char scmnd [ 20 ] ;
2020-04-21 08:41:35 +01:00
snprintf_P ( scmnd , sizeof ( scmnd ) , PSTR ( D_CMND_WIFICONFIG " 2 " ) ) ;
2020-03-02 19:32:55 +00:00
ExecuteCommand ( scmnd , SRC_BUTTON ) ;
2023-03-29 14:43:19 +01:00
return true ;
2020-03-02 19:32:55 +00:00
} else if ( ( buttonState = = SHT_PRESSED_EXT_HOLD_SIMULTANEOUS ) | | ( ( shutter_index_num_buttons = = 1 ) & & ( buttonState = = SHT_PRESSED_EXT_HOLD ) ) ) {
// no SetOption1 (0) checked above
// simultaneous or stand alone button extended hold detected
char scmnd [ 20 ] ;
snprintf_P ( scmnd , sizeof ( scmnd ) , PSTR ( D_CMND_RESET " 1 " ) ) ;
ExecuteCommand ( scmnd , SRC_BUTTON ) ;
2023-03-29 14:43:19 +01:00
return true ;
2020-03-02 19:32:55 +00:00
}
}
if ( buttonState < = SHT_PRESSED_IMMEDIATE ) {
2021-06-11 17:14:12 +01:00
if ( Settings - > shutter_startrelay [ shutter_index ] & & Settings - > shutter_startrelay [ shutter_index ] < 9 ) {
2020-01-22 12:23:59 +00:00
uint8_t pos_press_index = ( buttonState = = SHT_PRESSED_HOLD ) ? 3 : ( press_index - 1 ) ;
if ( pos_press_index > 3 ) pos_press_index = 3 ;
2021-06-05 10:47:09 +01:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: Shtr%d, Button %d = %d (single=1, double=2, tripple=3, hold=4) " ) , shutter_index + 1 , button_index + 1 , pos_press_index + 1 ) ;
2020-01-22 12:23:59 +00:00
XdrvMailbox . index = shutter_index + 1 ;
2020-10-30 11:29:48 +00:00
TasmotaGlobal . last_source = SRC_BUTTON ;
2020-01-22 12:23:59 +00:00
XdrvMailbox . data_len = 0 ;
char databuf [ 1 ] = " " ;
XdrvMailbox . data = databuf ;
XdrvMailbox . command = NULL ;
if ( buttonState = = SHT_PRESSED_IMMEDIATE ) {
2020-02-24 11:33:52 +00:00
XdrvMailbox . payload = XdrvMailbox . index ;
CmndShutterStop ( ) ;
2020-02-24 11:23:03 +00:00
} else {
2021-06-11 17:14:12 +01:00
uint8_t position = ( Settings - > shutter_button [ button_index ] > > ( 6 * pos_press_index + 2 ) ) & 0x03f ;
2020-01-22 12:23:59 +00:00
if ( position ) {
2020-09-09 13:04:57 +01:00
if ( Shutter [ shutter_index ] . direction ) {
2020-01-22 12:23:59 +00:00
XdrvMailbox . payload = XdrvMailbox . index ;
CmndShutterStop ( ) ;
} else {
XdrvMailbox . payload = position = ( position - 1 ) < < 1 ;
2021-02-18 11:42:59 +00:00
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shtr%d -> %d"), shutter_index+1, position);
2020-04-11 07:28:05 +01:00
if ( 102 = = position ) {
XdrvMailbox . payload = XdrvMailbox . index ;
CmndShutterToggle ( ) ;
} else {
2022-08-07 17:32:23 +01:00
if ( position = = ShutterRealToPercentPosition ( Shutter [ XdrvMailbox . index - 1 ] . real_position , XdrvMailbox . index - 1 ) ) {
Shutter [ XdrvMailbox . index - 1 ] . tilt_target_pos = position = = 0 ? Shutter [ XdrvMailbox . index - 1 ] . tilt_config [ 0 ] : ( position = = 100 ? Shutter [ XdrvMailbox . index - 1 ] . tilt_config [ 1 ] : Shutter [ XdrvMailbox . index - 1 ] . tilt_target_pos ) ;
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shtr%d -> Endpoint movement detected at %d. Set Tilt: %d"), shutter_index+1, position, Shutter[XdrvMailbox.index -1].tilt_target_pos);
}
2020-04-11 07:28:05 +01:00
CmndShutterPosition ( ) ;
}
2021-06-11 17:14:12 +01:00
if ( Settings - > shutter_button [ button_index ] & ( ( 0x01 < < 26 ) < < pos_press_index ) ) {
2020-01-22 12:23:59 +00:00
// MQTT broadcast to grouptopic
char scommand [ CMDSZ ] ;
char stopic [ TOPSZ ] ;
for ( uint32_t i = 0 ; i < MAX_SHUTTERS ; i + + ) {
2024-02-29 12:16:33 +00:00
if ( ( ( i = = shutter_index ) | | ( Settings - > shutter_button [ button_index ] & ( 0x01 < < 30 ) ) ) & & 0 = = ( Settings - > shutter_options [ i ] & 2 ) ) {
2020-01-22 12:23:59 +00:00
snprintf_P ( scommand , sizeof ( scommand ) , PSTR ( " ShutterPosition%d " ) , i + 1 ) ;
2020-03-28 10:13:01 +00:00
GetGroupTopic_P ( stopic , scommand , SET_MQTT_GRP_TOPIC ) ;
2020-01-22 12:23:59 +00:00
Response_P ( " %d " , position ) ;
MqttPublish ( stopic , false ) ;
}
2020-02-24 11:23:03 +00:00
} // for (uint32_t)
2021-06-11 17:14:12 +01:00
} // if (Settings->shutter)
2020-02-24 11:23:03 +00:00
} // ende else
} // if (position)
} // end else
2021-06-11 17:14:12 +01:00
} // if if (Settings->shutter_startrelay[shutter_index]
2020-01-02 10:23:11 +00:00
}
2020-01-22 12:23:59 +00:00
Response_P ( PSTR ( " { " ) ) ;
2020-03-02 19:32:55 +00:00
ResponseAppend_P ( JSON_SHUTTER_BUTTON , shutter_index + 1 , ( buttonState < = SHT_PRESSED_EXT_HOLD ) ? ( button_index + 1 ) : 0 , press_index ) ;
2020-01-22 12:23:59 +00:00
ResponseJsonEnd ( ) ;
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_STAT , PSTR ( D_PRFX_SHUTTER ) ) ;
2020-01-02 10:23:11 +00:00
}
2023-03-29 14:43:19 +01:00
return true ;
2020-01-02 10:23:11 +00:00
}
2020-01-04 14:09:57 +00:00
void ShutterSetPosition ( uint32_t device , uint32_t position )
2019-09-29 17:00:01 +01:00
{
char svalue [ 32 ] ; // Command and number parameter
snprintf_P ( svalue , sizeof ( svalue ) , PSTR ( D_PRFX_SHUTTER D_CMND_SHUTTER_POSITION " %d %d " ) , device , position ) ;
2020-09-05 19:39:24 +01:00
ExecuteCommand ( svalue , SRC_SHUTTER ) ;
2019-09-29 17:00:01 +01:00
}
2020-07-21 06:24:14 +01:00
void ShutterToggle ( bool dir )
{
2021-11-16 12:46:22 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " SHT: Toggle: %d, i %d, dir %d, lastdir %d " ) , XdrvMailbox . payload , XdrvMailbox . index , dir , Shutter [ XdrvMailbox . index - 1 ] . lastdirection ) ;
2020-07-21 06:24:14 +01:00
if ( ( 1 = = XdrvMailbox . index ) & & ( XdrvMailbox . payload ! = - 99 ) ) {
XdrvMailbox . index = XdrvMailbox . payload ;
}
2020-10-30 11:29:48 +00:00
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = TasmotaGlobal . shutters_present ) ) {
2020-07-21 06:24:14 +01:00
uint32_t index = XdrvMailbox . index - 1 ;
if ( dir ) {
2021-02-18 11:42:59 +00:00
XdrvMailbox . payload = ( Shutter [ index ] . direction = = 0 ? ( ( Shutter [ index ] . lastdirection > 0 ) ? 0 : 100 ) : ( Shutter [ index ] . direction > 0 ) ? 0 : 100 ) ;
2020-07-21 06:24:14 +01:00
}
else {
2020-09-09 13:04:57 +01:00
XdrvMailbox . payload = ( 50 < ShutterRealToPercentPosition ( Shutter [ index ] . real_position , index ) ) ? 0 : 100 ;
2020-07-21 06:24:14 +01:00
}
XdrvMailbox . data_len = 0 ;
2020-10-30 11:29:48 +00:00
TasmotaGlobal . last_source = SRC_WEBGUI ;
2020-07-21 06:24:14 +01:00
CmndShutterPosition ( ) ;
}
}
2023-05-21 11:25:11 +01:00
void ShutterShow ( ) {
for ( uint32_t i = 0 ; i < TasmotaGlobal . shutters_present ; i + + ) {
2023-08-03 10:55:20 +01:00
WSContentSend_P ( HTTP_MSG_SLIDER_SHUTTER , ( Settings - > shutter_options [ i ] & 1 ) ? D_OPEN : D_CLOSE , ( Settings - > shutter_options [ i ] & 1 ) ? D_CLOSE : D_OPEN , ( Settings - > shutter_options [ i ] & 1 ) ? ( 100 - ShutterRealToPercentPosition ( - 9999 , i ) ) : ShutterRealToPercentPosition ( - 9999 , i ) , i + 1 ) ;
2024-01-15 17:24:15 +00:00
WSContentSeparator ( 3 ) ; // Don't print separator on next WSContentSeparator(1)
2023-05-21 11:25:11 +01:00
}
}
2019-09-29 17:00:01 +01:00
/*********************************************************************************************\
* Commands
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void CmndShutterOpen ( void )
{
2021-01-23 15:26:23 +00:00
//AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Payload open: %d, i %d"), XdrvMailbox.payload, XdrvMailbox.index);
2022-12-10 22:52:53 +00:00
if ( ( ! XdrvMailbox . usridx ) & & ( XdrvMailbox . payload ! = - 99 ) ) {
2019-12-09 09:14:13 +00:00
XdrvMailbox . index = XdrvMailbox . payload ;
}
2019-09-29 17:00:01 +01:00
XdrvMailbox . payload = 100 ;
2020-10-30 11:29:48 +00:00
TasmotaGlobal . last_source = SRC_WEBGUI ;
2019-09-29 17:00:01 +01:00
CmndShutterPosition ( ) ;
}
2020-04-11 07:28:05 +01:00
void CmndShutterStopOpen ( void )
{
2020-10-30 11:29:48 +00:00
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = TasmotaGlobal . shutters_present ) ) {
2020-04-11 07:28:05 +01:00
uint32_t index = XdrvMailbox . index - 1 ;
2020-09-09 13:04:57 +01:00
if ( Shutter [ index ] . direction ) {
2020-04-11 07:28:05 +01:00
CmndShutterStop ( ) ;
} else {
CmndShutterOpen ( ) ;
}
}
}
2019-09-29 17:00:01 +01:00
void CmndShutterClose ( void )
{
2021-01-23 15:26:23 +00:00
//AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Payload close: %d, i %d"), XdrvMailbox.payload, XdrvMailbox.index);
2022-12-10 22:52:53 +00:00
if ( ( ! XdrvMailbox . usridx ) & & ( XdrvMailbox . payload ! = - 99 ) ) {
2019-12-09 09:14:13 +00:00
XdrvMailbox . index = XdrvMailbox . payload ;
}
2019-09-29 17:00:01 +01:00
XdrvMailbox . payload = 0 ;
2020-10-30 11:29:48 +00:00
TasmotaGlobal . last_source = SRC_WEBGUI ;
2019-09-29 17:00:01 +01:00
CmndShutterPosition ( ) ;
}
2020-04-11 07:28:05 +01:00
void CmndShutterStopClose ( void )
{
2020-10-30 11:29:48 +00:00
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = TasmotaGlobal . shutters_present ) ) {
2020-04-11 07:28:05 +01:00
uint32_t index = XdrvMailbox . index - 1 ;
2020-09-09 13:04:57 +01:00
if ( Shutter [ index ] . direction ) {
2020-04-11 07:28:05 +01:00
CmndShutterStop ( ) ;
} else {
CmndShutterClose ( ) ;
}
}
}
void CmndShutterToggle ( void )
{
2020-07-21 06:24:14 +01:00
ShutterToggle ( false ) ;
}
void CmndShutterToggleDir ( void )
{
ShutterToggle ( true ) ;
}
void CmndShutterStopToggle ( void )
{
2020-10-30 11:29:48 +00:00
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = TasmotaGlobal . shutters_present ) ) {
2020-04-11 07:28:05 +01:00
uint32_t index = XdrvMailbox . index - 1 ;
2020-09-09 13:04:57 +01:00
if ( Shutter [ index ] . direction ) {
2020-07-21 06:24:14 +01:00
CmndShutterStop ( ) ;
} else {
CmndShutterToggle ( ) ;
}
2020-04-11 07:28:05 +01:00
}
}
2020-07-21 06:24:14 +01:00
void CmndShutterStopToggleDir ( void )
2020-04-11 07:28:05 +01:00
{
2020-10-30 11:29:48 +00:00
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = TasmotaGlobal . shutters_present ) ) {
2020-04-11 07:28:05 +01:00
uint32_t index = XdrvMailbox . index - 1 ;
2020-09-09 13:04:57 +01:00
if ( Shutter [ index ] . direction ) {
2020-04-11 07:28:05 +01:00
CmndShutterStop ( ) ;
} else {
2020-07-21 06:24:14 +01:00
CmndShutterToggleDir ( ) ;
2020-04-11 07:28:05 +01:00
}
}
}
2019-09-29 17:00:01 +01:00
void CmndShutterStop ( void )
{
2020-10-30 11:29:48 +00:00
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = TasmotaGlobal . shutters_present ) ) {
2022-12-10 22:52:53 +00:00
//AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Try Stop %d: dir: %d"), XdrvMailbox.index, Shutter[XdrvMailbox.index -1].direction);
2021-06-11 17:14:12 +01:00
if ( ! ( Settings - > shutter_options [ XdrvMailbox . index - 1 ] & 2 ) ) {
2022-12-10 22:52:53 +00:00
if ( ( ! XdrvMailbox . usridx ) & & ( XdrvMailbox . payload ! = - 99 ) ) {
2020-01-12 13:18:15 +00:00
XdrvMailbox . index = XdrvMailbox . payload ;
}
uint32_t i = XdrvMailbox . index - 1 ;
2020-09-09 13:04:57 +01:00
if ( Shutter [ i ] . direction ! = 0 ) {
2020-01-12 13:18:15 +00:00
2022-12-10 22:52:53 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " SHT: Stop %d: dir: %d " ) , XdrvMailbox . index , Shutter [ i ] . direction ) ;
2020-12-18 08:19:45 +00:00
Shutter [ i ] . target_position = Shutter [ i ] . real_position ;
2023-05-03 20:44:09 +01:00
TasmotaGlobal . last_source = SRC_SHUTTER ;
2021-01-01 12:44:04 +00:00
}
2020-12-18 08:19:45 +00:00
if ( XdrvMailbox . command )
ResponseCmndDone ( ) ;
2021-02-18 11:42:59 +00:00
ShutterUpdatePosition ( ) ;
2019-09-29 17:00:01 +01:00
} else {
2020-01-04 15:10:03 +00:00
if ( XdrvMailbox . command )
2020-01-12 13:18:15 +00:00
ResponseCmndIdxChar ( " Locked " ) ;
2019-09-29 17:00:01 +01:00
}
}
}
2020-10-21 09:19:56 +01:00
void CmndShutterIncDec ( void )
{
2021-06-05 10:47:09 +01:00
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Change in: payload %s (%d), payload %d, idx %d, src %d"), XdrvMailbox.data , XdrvMailbox.data_len, XdrvMailbox.payload , XdrvMailbox.index, TasmotaGlobal.last_source );
2020-10-30 11:29:48 +00:00
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = TasmotaGlobal . shutters_present ) ) {
2020-10-21 09:19:56 +01:00
if ( XdrvMailbox . data_len > 0 ) {
XdrvMailbox . payload = ShutterRealToPercentPosition ( Shutter [ XdrvMailbox . index - 1 ] . target_position , XdrvMailbox . index - 1 ) + XdrvMailbox . payload ;
// limit position to boundaries
XdrvMailbox . payload = XdrvMailbox . payload < 0 ? 0 : ( XdrvMailbox . payload > 100 ? 100 : XdrvMailbox . payload ) ;
CmndShutterPosition ( ) ;
}
}
}
2019-09-29 17:00:01 +01:00
void CmndShutterPosition ( void )
{
2020-10-30 11:29:48 +00:00
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = TasmotaGlobal . shutters_present ) ) {
2021-06-11 17:14:12 +01:00
if ( ! ( Settings - > shutter_options [ XdrvMailbox . index - 1 ] & 2 ) ) {
2020-01-12 13:18:15 +00:00
uint32_t index = XdrvMailbox . index - 1 ;
//limit the payload
2022-12-10 22:52:53 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: Pos. payload <%s> (%d), payload %d, idx %d (%d), src %d " ) , XdrvMailbox . data , XdrvMailbox . data_len , XdrvMailbox . payload , XdrvMailbox . index , XdrvMailbox . usridx , TasmotaGlobal . last_source ) ;
2020-01-12 13:18:15 +00:00
2023-03-31 08:46:36 +01:00
if ( XdrvMailbox . data_len > = 3 ) {
// check if input is of format "position,tilt"
uint32_t i = 0 ;
char * str_ptr ;
char data_copy [ strlen ( XdrvMailbox . data ) + 1 ] ;
strncpy ( data_copy , XdrvMailbox . data , sizeof ( data_copy ) ) ; // Duplicate data as strtok_r will modify it.
// Loop through the data string, splitting on ',' seperators.
for ( char * str = strtok_r ( data_copy , " , " , & str_ptr ) ; str & & i < 2 ; str = strtok_r ( nullptr , " , " , & str_ptr ) , i + + ) {
switch ( i ) {
case 0 :
XdrvMailbox . payload = atoi ( str ) ;
break ;
case 1 :
Shutter [ index ] . tilt_target_pos_override = atoi ( str ) ;
break ;
}
}
}
2020-01-12 13:18:15 +00:00
// value 0 with data_len > 0 can mean Open
2024-02-18 11:36:03 +00:00
// special handling fo UP,DOWN,TOGGLE,STOP and similar commands command
//
if ( XdrvMailbox . data_len > 0 ) {
// set len to 0 to avoid loop
uint32_t data_len_save = XdrvMailbox . data_len ;
int32_t payload_save = XdrvMailbox . payload ;
XdrvMailbox . data_len = 0 ;
XdrvMailbox . payload = - 99 ;
2023-06-12 18:09:19 +01:00
if ( ! strcasecmp ( XdrvMailbox . data , D_CMND_SHUTTER_UP ) | | ! strcasecmp ( XdrvMailbox . data , D_CMND_SHUTTER_OPEN ) | | ( ( Shutter [ index ] . direction = = 0 ) & & ! strcasecmp ( XdrvMailbox . data , D_CMND_SHUTTER_STOPOPEN ) ) ) {
2020-01-12 13:18:15 +00:00
CmndShutterOpen ( ) ;
return ;
}
2023-06-12 18:09:19 +01:00
if ( ! strcasecmp ( XdrvMailbox . data , D_CMND_SHUTTER_DOWN ) | | ! strcasecmp ( XdrvMailbox . data , D_CMND_SHUTTER_CLOSE ) | | ( ( Shutter [ index ] . direction = = 0 ) & & ! strcasecmp ( XdrvMailbox . data , D_CMND_SHUTTER_STOPCLOSE ) ) ) {
2020-01-12 13:18:15 +00:00
CmndShutterClose ( ) ;
return ;
}
2020-04-12 09:36:24 +01:00
if ( ! strcasecmp ( XdrvMailbox . data , D_CMND_SHUTTER_TOGGLE ) ) {
CmndShutterToggle ( ) ;
return ;
}
2020-07-21 06:24:14 +01:00
if ( ! strcasecmp ( XdrvMailbox . data , D_CMND_SHUTTER_TOGGLEDIR ) ) {
CmndShutterToggleDir ( ) ;
return ;
}
2020-09-09 13:04:57 +01:00
if ( ! strcasecmp ( XdrvMailbox . data , D_CMND_SHUTTER_STOP ) | | ( ( Shutter [ index ] . direction ) & & ( ! strcasecmp ( XdrvMailbox . data , D_CMND_SHUTTER_STOPOPEN ) | | ! strcasecmp ( XdrvMailbox . data , D_CMND_SHUTTER_STOPCLOSE ) ) ) ) {
2020-01-12 13:18:15 +00:00
CmndShutterStop ( ) ;
return ;
}
2024-02-18 11:36:03 +00:00
// restore values
XdrvMailbox . payload = payload_save ;
XdrvMailbox . data_len = data_len_save ;
2020-01-04 01:36:05 +00:00
}
2019-12-02 20:44:05 +00:00
2020-09-09 13:04:57 +01:00
int8_t target_pos_percent = ( XdrvMailbox . payload < 0 ) ? ( XdrvMailbox . payload = = - 99 ? ShutterRealToPercentPosition ( Shutter [ index ] . real_position , index ) : 0 ) : ( ( XdrvMailbox . payload > 100 ) ? 100 : XdrvMailbox . payload ) ;
2024-06-21 15:35:05 +01:00
target_pos_percent = ( ( Settings - > shutter_options [ index ] & 1 ) & & ( ( SRC_SERIAL ! = TasmotaGlobal . last_source )
& & ( SRC_WEBGUI ! = TasmotaGlobal . last_source )
& & ( SRC_WEBCOMMAND ! = TasmotaGlobal . last_source )
) ) ? 100 - target_pos_percent : target_pos_percent ;
2023-08-24 11:39:42 +01:00
// if position is either 0 or 100 reset the tilt to avoid tilt moving at the end
if ( target_pos_percent = = 0 & & ShutterRealToPercentPosition ( Shutter [ index ] . real_position , index ) > 0 ) { Shutter [ index ] . tilt_target_pos = Shutter [ index ] . tilt_config [ 4 ] ; }
if ( target_pos_percent = = 100 & & ShutterRealToPercentPosition ( Shutter [ index ] . real_position , index ) < 100 ) { Shutter [ index ] . tilt_target_pos = Shutter [ index ] . tilt_config [ 3 ] ; }
2023-11-03 15:01:57 +00:00
// manual override of tiltposition
if ( Shutter [ index ] . tilt_target_pos_override ! = - 128 ) {
Shutter [ index ] . tilt_target_pos = tmin ( tmax ( Shutter [ index ] . tilt_config [ 0 ] , Shutter [ index ] . tilt_target_pos_override ) , Shutter [ index ] . tilt_config [ 1 ] ) ;
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: Override tilt set: %d --> %d " ) , Shutter [ index ] . tilt_target_pos_override , Shutter [ index ] . tilt_target_pos ) ;
Shutter [ index ] . tilt_target_pos_override = - 128 ;
}
2020-01-12 13:18:15 +00:00
if ( XdrvMailbox . payload ! = - 99 ) {
2021-06-11 17:14:12 +01:00
//target_pos_percent = (Settings->shutter_options[index] & 1) ? 100 - target_pos_percent : target_pos_percent;
2020-09-09 13:04:57 +01:00
Shutter [ index ] . target_position = ShutterPercentToRealPosition ( target_pos_percent , index ) ;
//Shutter[i].accelerator[index] = ShutterGlobal.open_velocity_max / ((Shutter[i].motordelay[index] > 0) ? Shutter[i].motordelay[index] : 1);
2021-06-11 17:14:12 +01:00
//Shutter[i].target_position[index] = XdrvMailbox.payload < 5 ? Settings->shuttercoeff[2][index] * XdrvMailbox.payload : Settings->shuttercoeff[1][index] * XdrvMailbox.payload + Settings->shuttercoeff[0,index];
2021-11-07 14:53:12 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: lastsource %d:, real %d, target %d, tiltreal: %d, tilttarget: %d, payload %d " ) , TasmotaGlobal . last_source , Shutter [ index ] . real_position , Shutter [ index ] . target_position , Shutter [ index ] . tilt_real_pos , Shutter [ index ] . tilt_target_pos , target_pos_percent ) ;
2020-01-12 13:18:15 +00:00
}
2021-11-07 14:53:12 +00:00
if ( ( target_pos_percent > = 0 ) & & ( target_pos_percent < = 100 ) & &
2021-11-14 21:17:28 +00:00
( abs ( Shutter [ index ] . target_position - Shutter [ index ] . real_position ) > Shutter [ index ] . min_realPositionChange | |
abs ( Shutter [ index ] . tilt_target_pos - Shutter [ index ] . tilt_real_pos ) > Shutter [ index ] . min_TiltChange ) ) {
2021-06-11 17:14:12 +01:00
if ( Settings - > shutter_options [ index ] & 4 ) {
2023-03-16 13:38:04 +00:00
if ( 0 = = target_pos_percent & & Shutter [ index ] . real_position > 0 ) Shutter [ index ] . target_position - = 1 * RESOLUTION * STEPS_PER_SECOND ;
if ( 100 = = target_pos_percent & & Shutter [ index ] . real_position < Shutter [ index ] . open_max ) Shutter [ index ] . target_position + = 1 * RESOLUTION * STEPS_PER_SECOND ;
2020-01-27 08:46:39 +00:00
}
2021-11-14 21:17:28 +00:00
int8_t new_shutterdirection ;
if ( abs ( Shutter [ index ] . target_position - Shutter [ index ] . real_position ) > Shutter [ index ] . min_realPositionChange ) {
new_shutterdirection = Shutter [ index ] . real_position < Shutter [ index ] . target_position ? 1 : - 1 ;
2021-11-15 17:43:47 +00:00
Shutter [ index ] . tiltmoving = 0 ;
2021-11-14 21:17:28 +00:00
} else {
2021-11-07 14:53:12 +00:00
new_shutterdirection = Shutter [ index ] . tilt_real_pos < Shutter [ index ] . tilt_target_pos ? 1 : - 1 ;
2021-11-15 17:43:47 +00:00
Shutter [ index ] . tiltmoving = 1 ;
2021-11-07 14:53:12 +00:00
}
2021-11-14 21:17:28 +00:00
2020-09-09 13:04:57 +01:00
if ( Shutter [ index ] . direction = = - new_shutterdirection ) {
2021-11-14 21:17:28 +00:00
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Stop shutter to reverse direction"));
2020-09-05 19:39:24 +01:00
ShutterPowerOff ( index ) ;
2019-09-29 17:00:01 +01:00
}
2020-09-09 13:04:57 +01:00
if ( Shutter [ index ] . direction ! = new_shutterdirection ) {
ShutterStartInit ( index , new_shutterdirection , Shutter [ index ] . target_position ) ;
2022-02-04 08:04:03 +00:00
uint8_t save_direction = Shutter [ index ] . direction ;
Shutter [ index ] . direction = 0 ; // set temporary direction = 0 to avoid RTC timer sarting. Some delay may happen before shutter starts moving
2020-09-09 13:04:57 +01:00
switch ( ShutterGlobal . position_mode ) {
2020-09-05 19:39:24 +01:00
case SHT_COUNTER :
case SHT_PWM_TIME :
case SHT_PWM_VALUE :
case SHT_TIME_UP_DOWN :
2020-09-09 13:04:57 +01:00
if ( ! ShutterGlobal . skip_relay_change ) {
2020-09-05 19:39:24 +01:00
// Code for shutters with circuit safe configuration, switch the direction Relay
2021-06-11 17:14:12 +01:00
ExecuteCommandPowerShutter ( Settings - > shutter_startrelay [ index ] + 1 , new_shutterdirection = = 1 ? 0 : 1 , SRC_SHUTTER ) ;
2022-02-04 07:58:42 +00:00
delay ( SHUTTER_RELAY_OPERATION_TIME ) ;
2020-09-05 19:39:24 +01:00
// power on
2021-06-11 17:14:12 +01:00
ExecuteCommandPowerShutter ( Settings - > shutter_startrelay [ index ] , 1 , SRC_SHUTTER ) ;
2020-06-22 12:21:13 +01:00
}
2021-06-11 17:14:12 +01:00
//if (ShutterGlobal.position_mode != SHT_TIME_UP_DOWN) ExecuteCommandPowerShutter(Settings->shutter_startrelay[index]+2, 1, SRC_SHUTTER);
2020-09-05 19:39:24 +01:00
break ;
case SHT_TIME :
2020-09-09 13:04:57 +01:00
if ( ! ShutterGlobal . skip_relay_change ) {
2021-06-11 17:14:12 +01:00
if ( ( TasmotaGlobal . power > > ( Settings - > shutter_startrelay [ index ] - 1 ) ) & 3 > 0 ) {
ExecuteCommandPowerShutter ( Settings - > shutter_startrelay [ index ] + ( new_shutterdirection = = 1 ? 1 : 0 ) , Shutter [ index ] . switch_mode = = SHT_SWITCH ? 0 : 1 , SRC_SHUTTER ) ;
2020-09-05 19:39:24 +01:00
}
2021-06-11 17:14:12 +01:00
ExecuteCommandPowerShutter ( Settings - > shutter_startrelay [ index ] + ( new_shutterdirection = = 1 ? 0 : 1 ) , 1 , SRC_SHUTTER ) ;
2020-09-05 19:39:24 +01:00
}
break ;
case SHT_TIME_GARAGE :
2020-09-09 13:04:57 +01:00
if ( ! ShutterGlobal . skip_relay_change ) {
if ( new_shutterdirection = = Shutter [ index ] . lastdirection ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " SHT: Garage not move in this direction: %d " ) , Shutter [ index ] . switch_mode = = SHT_PULSE ) ;
2020-09-09 13:04:57 +01:00
for ( uint8_t k = 0 ; k < = ( uint8_t ) ( Shutter [ index ] . switch_mode = = SHT_PULSE ) ; k + + ) {
2021-06-11 17:14:12 +01:00
ExecuteCommandPowerShutter ( Settings - > shutter_startrelay [ index ] , 1 , SRC_SHUTTER ) ;
2022-12-14 10:25:41 +00:00
ShutterWaitForMotorStop ( index ) ;
2021-06-11 17:14:12 +01:00
ExecuteCommandPowerShutter ( Settings - > shutter_startrelay [ index ] , 0 , SRC_SHUTTER ) ;
2022-12-14 10:25:41 +00:00
ShutterWaitForMotorStop ( index ) ;
2020-09-05 19:39:24 +01:00
}
// reset shutter time to avoid 2 seconds above count as runtime
2020-09-09 13:04:57 +01:00
Shutter [ index ] . time = 0 ;
} // if (new_shutterdirection == Shutter[i].lastdirection[index])
2021-06-11 17:14:12 +01:00
ExecuteCommandPowerShutter ( Settings - > shutter_startrelay [ index ] , 1 , SRC_SHUTTER ) ;
2020-09-09 13:04:57 +01:00
} // if (!ShutterGlobal.skip_relay_change)
2020-09-05 19:39:24 +01:00
break ;
2020-09-09 13:04:57 +01:00
} // switch (ShutterGlobal.position_mode)
2022-02-04 08:04:03 +00:00
Shutter [ index ] . direction = save_direction ;
2020-09-09 13:04:57 +01:00
ShutterGlobal . RelayCurrentMask = 0 ;
} // if (Shutter[i].direction[index] != new_shutterdirection)
2020-01-12 13:18:15 +00:00
} else {
2020-09-09 13:04:57 +01:00
target_pos_percent = ShutterRealToPercentPosition ( Shutter [ index ] . real_position , index ) ;
2019-09-29 17:00:01 +01:00
}
2022-12-10 22:52:53 +00:00
index = ( ! XdrvMailbox . usridx & & ! XdrvMailbox . data_len ) ? MAX_SHUTTERS : index ;
ShutterReportPosition ( true , index ) ;
ShutterGlobal . start_reported = 1 ;
2020-01-12 13:18:15 +00:00
XdrvMailbox . index = index + 1 ; // Fix random index for ShutterClose
2019-09-29 17:00:01 +01:00
} else {
2020-04-21 15:19:42 +01:00
ShutterReportPosition ( true , MAX_SHUTTERS ) ;
2020-01-12 13:18:15 +00:00
if ( XdrvMailbox . command )
ResponseCmndIdxChar ( " Locked " ) ;
2019-09-29 17:00:01 +01:00
}
}
}
2020-04-11 07:28:05 +01:00
void CmndShutterStopPosition ( void )
{
2020-10-30 11:29:48 +00:00
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = TasmotaGlobal . shutters_present ) ) {
2020-04-11 07:28:05 +01:00
uint32_t index = XdrvMailbox . index - 1 ;
2020-09-09 13:04:57 +01:00
if ( Shutter [ index ] . direction ) {
2020-04-11 07:28:05 +01:00
XdrvMailbox . payload = - 99 ;
CmndShutterStop ( ) ;
} else {
CmndShutterPosition ( ) ;
}
}
}
2021-11-14 21:17:28 +00:00
2019-09-29 17:00:01 +01:00
void CmndShutterOpenTime ( void )
{
2020-10-30 11:29:48 +00:00
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = TasmotaGlobal . shutters_present ) ) {
2019-09-29 17:00:01 +01:00
if ( XdrvMailbox . data_len > 0 ) {
2021-06-11 17:14:12 +01:00
Settings - > shutter_opentime [ XdrvMailbox . index - 1 ] = ( uint16_t ) ( 10 * CharToFloat ( XdrvMailbox . data ) ) ;
2019-09-29 17:00:01 +01:00
ShutterInit ( ) ;
}
2023-01-08 13:35:09 +00:00
/*
2019-09-29 17:00:01 +01:00
char time_chr [ 10 ] ;
2021-06-11 17:14:12 +01:00
dtostrfd ( ( float ) ( Settings - > shutter_opentime [ XdrvMailbox . index - 1 ] ) / 10 , 1 , time_chr ) ;
2019-09-29 17:00:01 +01:00
ResponseCmndIdxChar ( time_chr ) ;
2023-01-08 13:35:09 +00:00
*/
ResponseCmndIdxFloat ( ( float ) ( Settings - > shutter_opentime [ XdrvMailbox . index - 1 ] ) / 10 , 1 ) ;
2019-09-29 17:00:01 +01:00
}
}
void CmndShutterCloseTime ( void )
{
2020-10-30 11:29:48 +00:00
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = TasmotaGlobal . shutters_present ) ) {
2019-09-29 17:00:01 +01:00
if ( XdrvMailbox . data_len > 0 ) {
2021-06-11 17:14:12 +01:00
Settings - > shutter_closetime [ XdrvMailbox . index - 1 ] = ( uint16_t ) ( 10 * CharToFloat ( XdrvMailbox . data ) ) ;
2019-09-29 17:00:01 +01:00
ShutterInit ( ) ;
}
2023-01-08 13:35:09 +00:00
/*
2019-09-29 17:00:01 +01:00
char time_chr [ 10 ] ;
2021-06-11 17:14:12 +01:00
dtostrfd ( ( float ) ( Settings - > shutter_closetime [ XdrvMailbox . index - 1 ] ) / 10 , 1 , time_chr ) ;
2019-09-29 17:00:01 +01:00
ResponseCmndIdxChar ( time_chr ) ;
2023-01-08 13:35:09 +00:00
*/
ResponseCmndIdxFloat ( ( float ) ( Settings - > shutter_closetime [ XdrvMailbox . index - 1 ] ) / 10 , 1 ) ;
2019-09-29 17:00:01 +01:00
}
}
2019-10-30 08:28:46 +00:00
void CmndShutterMotorDelay ( void )
{
2020-10-30 11:29:48 +00:00
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = TasmotaGlobal . shutters_present ) ) {
2019-10-30 08:28:46 +00:00
if ( XdrvMailbox . data_len > 0 ) {
2021-09-03 11:57:51 +01:00
Settings - > shutter_motordelay [ XdrvMailbox . index - 1 ] = ( uint8_t ) ( STEPS_PER_SECOND * CharToFloat ( XdrvMailbox . data ) ) ;
2019-10-30 08:28:46 +00:00
ShutterInit ( ) ;
2021-11-07 14:53:12 +00:00
//AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Shtr Init1. realdelay %d"),Shutter[XdrvMailbox.index -1].motordelay);
2019-10-30 08:28:46 +00:00
}
2023-01-08 13:35:09 +00:00
/*
2019-10-30 08:28:46 +00:00
char time_chr [ 10 ] ;
2021-09-03 11:57:51 +01:00
dtostrfd ( ( float ) ( Shutter [ XdrvMailbox . index - 1 ] . motordelay ) / STEPS_PER_SECOND , 2 , time_chr ) ;
2019-10-30 08:28:46 +00:00
ResponseCmndIdxChar ( time_chr ) ;
2023-01-08 13:35:09 +00:00
*/
ResponseCmndIdxFloat ( ( float ) ( Shutter [ XdrvMailbox . index - 1 ] . motordelay ) / STEPS_PER_SECOND , 2 ) ;
2019-10-30 08:28:46 +00:00
}
}
2020-09-05 19:39:24 +01:00
void CmndShutterMode ( void )
{
2022-12-14 10:25:41 +00:00
if ( ! XdrvMailbox . usridx ) {
2022-12-08 11:55:34 +00:00
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < = MAX_MODES ) ) {
2022-12-14 10:25:41 +00:00
Settings - > shutter_mode = ShutterGlobal . position_mode = XdrvMailbox . payload ;
2022-12-08 11:55:34 +00:00
ShutterInit ( ) ;
}
ResponseCmndNumber ( ShutterGlobal . position_mode ) ;
2020-09-05 19:39:24 +01:00
}
}
2019-09-29 17:00:01 +01:00
void CmndShutterRelay ( void )
{
2022-12-14 10:25:41 +00:00
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < = 32 ) & & ( XdrvMailbox . index < = MAX_SHUTTERS ) ) {
//Settings->shutter_startrelay[XdrvMailbox.index -1] = XdrvMailbox.payload;
2022-12-08 17:54:45 +00:00
if ( XdrvMailbox . payload > 0 ) {
ShutterGlobal . RelayShutterMask | = 3 < < ( XdrvMailbox . payload - 1 ) ;
} else {
ShutterGlobal . RelayShutterMask ^ = 3 < < ( Settings - > shutter_startrelay [ XdrvMailbox . index - 1 ] - 1 ) ;
2019-09-29 17:00:01 +01:00
}
2022-12-21 14:26:42 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " SHT: relold:%d index:%d, mode:%d, relaymask: %ld " ) ,
2022-12-14 10:25:41 +00:00
Settings - > shutter_startrelay [ XdrvMailbox . index - 1 ] , XdrvMailbox . index , Settings - > shutter_mode , ShutterGlobal . RelayShutterMask ) ;
if ( Settings - > shutter_startrelay [ XdrvMailbox . index - 1 ] = = 0 & & XdrvMailbox . index = = 1 & & Settings - > shutter_mode = = SHT_UNDEF ) {
// first shutter was not defined, maybe init
Settings - > shutter_mode = SHT_AUTOCONFIG ;
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " SHT: Autoconfig " ) ) ;
}
2022-12-08 17:54:45 +00:00
Settings - > shutter_startrelay [ XdrvMailbox . index - 1 ] = XdrvMailbox . payload ;
2022-12-14 10:25:41 +00:00
2022-12-08 17:54:45 +00:00
ShutterInit ( ) ;
// if payload is 0 to disable the relay there must be a reboot. Otherwhise does not work
}
uint32_t start = ( ! XdrvMailbox . usridx & & ! XdrvMailbox . data_len ) ? 0 : XdrvMailbox . index - 1 ;
uint32_t end = ( ! XdrvMailbox . usridx & & ! XdrvMailbox . data_len ) ? TasmotaGlobal . shutters_present : XdrvMailbox . index ;
// {"ShutterRelay1":"1","ShutterRelay2":"3","ShutterRelay3":"5"}
Response_P ( PSTR ( " { " ) ) ;
for ( uint32_t i = start ; i < end ; i + + ) {
2023-01-08 13:35:09 +00:00
ResponseAppend_P ( PSTR ( " %s \" " D_PRFX_SHUTTER D_CMND_SHUTTER_RELAY " %d \" :%d " ) , ( i > start ) ? " , " : " " , i + 1 , Settings - > shutter_startrelay [ i ] ) ;
2019-09-29 17:00:01 +01:00
}
2022-12-08 17:54:45 +00:00
ResponseAppend_P ( PSTR ( " } " ) ) ;
2019-09-29 17:00:01 +01:00
}
2020-01-02 10:23:11 +00:00
void CmndShutterButton ( void )
{
2020-01-04 15:24:45 +00:00
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = MAX_SHUTTERS ) ) {
2020-01-02 10:23:11 +00:00
uint32_t setting = 0 ;
// (setting>>31)&(0x01) : enabled
// (setting>>30)&(0x01) : mqtt broadcast to all index
// (setting>>29)&(0x01) : mqtt broadcast hold
// (setting>>28)&(0x01) : mqtt broadcast tripple press
// (setting>>27)&(0x01) : mqtt broadcast double press
// (setting>>26)&(0x01) : mqtt broadcast single press
2020-04-11 07:28:05 +01:00
// (setting>>20)&(0x3f) : shutter_position hold; 0 disabled, 1..101 == 0..100%, 102 == toggle
// (setting>>14)&(0x3f) : shutter_position tripple press 0 disabled, 1..101 == 0..100%, 102 == toggle
// (setting>> 8)&(0x3f) : shutter_position double press 0 disabled, 1..101 == 0..100%, 102 == toggle
// (setting>> 2)&(0x3f) : shutter_position single press 0 disabled, 1..101 == 0..100%, 102 == toggle
2020-01-02 10:23:11 +00:00
// (setting>> 0)&(0x03) : shutter_index
if ( XdrvMailbox . data_len > 0 ) {
2020-01-09 10:35:01 +00:00
uint32_t i = 0 ;
uint32_t button_index = 0 ;
bool done = false ;
bool isShortCommand = false ;
2020-01-02 10:23:11 +00:00
char * str_ptr ;
2020-01-09 10:35:01 +00:00
char data_copy [ strlen ( XdrvMailbox . data ) + 1 ] ;
strncpy ( data_copy , XdrvMailbox . data , sizeof ( data_copy ) ) ; // Duplicate data as strtok_r will modify it.
// Loop through the data string, splitting on ' ' seperators.
for ( char * str = strtok_r ( data_copy , " " , & str_ptr ) ; str & & i < ( 1 + 4 + 4 + 1 ) ; str = strtok_r ( nullptr , " " , & str_ptr ) , i + + ) {
2020-01-02 10:23:11 +00:00
int field ;
2020-04-11 07:28:05 +01:00
switch ( str [ 0 ] ) {
case ' - ' :
field = - 1 ;
break ;
case ' t ' :
field = 102 ;
break ;
default :
field = atoi ( str ) ;
break ;
2020-01-09 10:35:01 +00:00
}
2020-01-02 10:23:11 +00:00
switch ( i ) {
case 0 :
2020-01-04 15:24:45 +00:00
if ( ( field > = - 1 ) & & ( field < = 4 ) ) {
button_index = ( field < = 0 ) ? ( - 1 ) : field ;
done = ( button_index = = - 1 ) ;
2020-01-02 10:23:11 +00:00
} else
done = true ;
break ;
case 1 :
if ( ! strcmp_P ( str , PSTR ( " up " ) ) ) {
2020-01-09 09:35:27 +00:00
setting | = ( ( ( 100 > > 1 ) + 1 ) < < 2 ) | ( ( ( 50 > > 1 ) + 1 ) < < 8 ) | ( ( ( 75 > > 1 ) + 1 ) < < 14 ) | ( ( ( 100 > > 1 ) + 1 ) < < 20 ) ;
isShortCommand = true ;
2020-01-02 10:23:11 +00:00
break ;
} else if ( ! strcmp_P ( str , PSTR ( " down " ) ) ) {
2020-01-09 09:35:27 +00:00
setting | = ( ( ( 0 > > 1 ) + 1 ) < < 2 ) | ( ( ( 50 > > 1 ) + 1 ) < < 8 ) | ( ( ( 25 > > 1 ) + 1 ) < < 14 ) | ( ( ( 0 > > 1 ) + 1 ) < < 20 ) ;
isShortCommand = true ;
2020-01-02 10:23:11 +00:00
break ;
} else if ( ! strcmp_P ( str , PSTR ( " updown " ) ) ) {
setting | = ( ( ( 100 > > 1 ) + 1 ) < < 2 ) | ( ( ( 0 > > 1 ) + 1 ) < < 8 ) | ( ( ( 50 > > 1 ) + 1 ) < < 14 ) ;
2020-01-09 09:35:27 +00:00
isShortCommand = true ;
2020-01-02 10:23:11 +00:00
break ;
2020-04-11 07:28:05 +01:00
} else if ( ! strcmp_P ( str , PSTR ( " toggle " ) ) ) {
setting | = ( ( ( 102 > > 1 ) + 1 ) < < 2 ) | ( ( ( 50 > > 1 ) + 1 ) < < 8 ) ;
isShortCommand = true ;
break ;
2020-01-02 10:23:11 +00:00
}
case 2 :
2020-01-09 09:35:27 +00:00
if ( isShortCommand ) {
if ( ( field = = 1 ) & & ( setting & ( 0x3F < < ( 2 + 6 * 3 ) ) ) )
2020-04-11 07:28:05 +01:00
// if short command up or down (hold press position set) then also enable MQTT broadcast
2020-01-09 09:35:27 +00:00
setting | = ( 0x3 < < 29 ) ;
done = true ;
break ;
}
2020-01-02 10:23:11 +00:00
case 3 :
case 4 :
2020-04-11 07:28:05 +01:00
if ( ( field > = - 1 ) & & ( field < = 102 ) )
2020-01-02 10:23:11 +00:00
setting | = ( ( ( field > > 1 ) + 1 ) < < ( i * 6 + ( 2 - 6 ) ) ) ;
break ;
case 5 :
case 6 :
case 7 :
case 8 :
case 9 :
if ( field = = 1 )
setting | = ( 1 < < ( i + ( 26 - 5 ) ) ) ;
break ;
}
if ( done ) break ;
}
2020-01-04 15:24:45 +00:00
if ( button_index ) {
if ( button_index = = - 1 ) {
// remove all buttons for this shutter
2021-02-05 11:27:59 +00:00
for ( uint32_t i = 0 ; i < MAX_SHUTTER_KEYS ; i + + )
2021-06-11 17:14:12 +01:00
if ( ( Settings - > shutter_button [ i ] & 0x3 ) = = ( XdrvMailbox . index - 1 ) )
Settings - > shutter_button [ i ] = 0 ;
2020-01-04 15:24:45 +00:00
} else {
if ( setting ) {
// anything was set
setting | = ( 1 < < 31 ) ;
setting | = ( XdrvMailbox . index - 1 ) & 0x3 ;
}
2021-06-11 17:14:12 +01:00
Settings - > shutter_button [ button_index - 1 ] = setting ;
2020-01-04 15:24:45 +00:00
}
}
}
2021-02-05 11:27:59 +00:00
char setting_chr [ 30 * MAX_SHUTTER_KEYS ] = " - " , * setting_chr_ptr = setting_chr ;
for ( uint32_t i = 0 ; i < MAX_SHUTTER_KEYS ; i + + ) {
2021-06-11 17:14:12 +01:00
setting = Settings - > shutter_button [ i ] ;
2020-01-04 15:24:45 +00:00
if ( ( setting & ( 1 < < 31 ) ) & & ( ( setting & 0x3 ) = = ( XdrvMailbox . index - 1 ) ) ) {
if ( * setting_chr_ptr = = 0 )
setting_chr_ptr + = sprintf_P ( setting_chr_ptr , PSTR ( " | " ) ) ;
setting_chr_ptr + = snprintf_P ( setting_chr_ptr , 2 , PSTR ( " %d " ) , i + 1 ) ;
for ( uint32_t j = 0 ; j < 4 ; j + + ) {
int8_t pos = ( ( ( setting > > ( 2 + 6 * j ) ) & ( 0x3f ) ) - 1 ) < < 1 ;
2020-04-11 07:28:05 +01:00
if ( 0 < = pos )
if ( 102 = = pos ) {
setting_chr_ptr + = sprintf_P ( setting_chr_ptr , PSTR ( " t " ) ) ;
} else {
setting_chr_ptr + = snprintf_P ( setting_chr_ptr , 5 , PSTR ( " %d " ) , pos ) ;
}
2020-01-04 15:24:45 +00:00
else
setting_chr_ptr + = sprintf_P ( setting_chr_ptr , PSTR ( " - " ) ) ;
}
for ( uint32_t j = 0 ; j < 5 ; j + + ) {
bool mqtt = ( ( setting > > ( 26 + j ) ) & ( 0x01 ) ! = 0 ) ;
if ( mqtt )
setting_chr_ptr + = sprintf_P ( setting_chr_ptr , PSTR ( " 1 " ) ) ;
else
setting_chr_ptr + = sprintf_P ( setting_chr_ptr , PSTR ( " - " ) ) ;
2020-01-02 10:23:11 +00:00
}
}
}
2020-01-04 15:24:45 +00:00
ResponseCmndIdxChar ( setting_chr ) ;
2020-01-02 10:23:11 +00:00
}
}
2019-09-29 17:00:01 +01:00
void CmndShutterSetHalfway ( void )
{
2020-10-30 11:29:48 +00:00
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = TasmotaGlobal . shutters_present ) ) {
2019-09-29 17:00:01 +01:00
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < = 100 ) ) {
2021-06-11 17:14:12 +01:00
Settings - > shutter_set50percent [ XdrvMailbox . index - 1 ] = ( Settings - > shutter_options [ XdrvMailbox . index - 1 ] & 1 ) ? 100 - XdrvMailbox . payload : XdrvMailbox . payload ;
Settings - > shuttercoeff [ 0 ] [ XdrvMailbox . index - 1 ] = 0 ;
2019-09-29 17:00:01 +01:00
ShutterInit ( ) ;
}
2021-06-11 17:14:12 +01:00
ResponseCmndIdxNumber ( ( Settings - > shutter_options [ XdrvMailbox . index - 1 ] & 1 ) ? 100 - Settings - > shutter_set50percent [ XdrvMailbox . index - 1 ] : Settings - > shutter_set50percent [ XdrvMailbox . index - 1 ] ) ;
2019-09-29 17:00:01 +01:00
}
}
2019-12-04 11:34:27 +00:00
void CmndShutterFrequency ( void )
{
2019-12-15 13:21:41 +00:00
if ( ( XdrvMailbox . payload > 0 ) & & ( XdrvMailbox . payload < = 20000 ) ) {
2020-09-09 13:04:57 +01:00
ShutterGlobal . open_velocity_max = XdrvMailbox . payload ;
2020-10-30 11:29:48 +00:00
if ( TasmotaGlobal . shutters_present < 4 ) {
2021-06-11 17:14:12 +01:00
Settings - > shuttercoeff [ 4 ] [ 3 ] = ShutterGlobal . open_velocity_max ;
2019-12-14 11:09:35 +00:00
}
2019-12-20 10:27:35 +00:00
ShutterInit ( ) ;
2019-12-04 11:34:27 +00:00
}
2020-09-09 13:04:57 +01:00
ResponseCmndNumber ( ShutterGlobal . open_velocity_max ) ;
2019-12-04 11:34:27 +00:00
}
2019-09-29 17:00:01 +01:00
void CmndShutterSetClose ( void )
{
2020-10-30 11:29:48 +00:00
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = TasmotaGlobal . shutters_present ) ) {
2020-09-09 13:04:57 +01:00
Shutter [ XdrvMailbox . index - 1 ] . real_position = 0 ;
2021-11-07 14:53:12 +00:00
Shutter [ XdrvMailbox . index - 1 ] . tilt_real_pos = Shutter [ XdrvMailbox . index - 1 ] . tilt_config [ 0 ] ;
2023-05-03 20:44:09 +01:00
Shutter [ XdrvMailbox . index - 1 ] . lastdirection = - 1 ;
2019-10-01 16:19:37 +01:00
ShutterStartInit ( XdrvMailbox . index - 1 , 0 , 0 ) ;
2021-06-11 17:14:12 +01:00
Settings - > shutter_position [ XdrvMailbox . index - 1 ] = 0 ;
2019-12-12 15:14:06 +00:00
ResponseCmndIdxChar ( D_CONFIGURATION_RESET ) ;
2019-09-29 17:00:01 +01:00
}
}
2020-04-21 15:20:20 +01:00
void CmndShutterSetOpen ( void )
{
2020-10-30 11:29:48 +00:00
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = TasmotaGlobal . shutters_present ) ) {
2020-09-09 13:04:57 +01:00
Shutter [ XdrvMailbox . index - 1 ] . real_position = Shutter [ XdrvMailbox . index - 1 ] . open_max ;
2021-11-07 14:53:12 +00:00
Shutter [ XdrvMailbox . index - 1 ] . tilt_real_pos = Shutter [ XdrvMailbox . index - 1 ] . tilt_config [ 1 ] ;
2023-05-03 20:44:09 +01:00
Shutter [ XdrvMailbox . index - 1 ] . lastdirection = 1 ;
2020-09-09 13:04:57 +01:00
ShutterStartInit ( XdrvMailbox . index - 1 , 0 , Shutter [ XdrvMailbox . index - 1 ] . open_max ) ;
2021-06-11 17:14:12 +01:00
Settings - > shutter_position [ XdrvMailbox . index - 1 ] = 100 ;
2020-04-21 15:20:20 +01:00
ResponseCmndIdxChar ( D_CONFIGURATION_RESET ) ;
2019-09-29 17:00:01 +01:00
}
}
2020-09-09 14:24:21 +01:00
void CmndShutterPwmRange ( void )
{
2020-10-30 11:29:48 +00:00
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = TasmotaGlobal . shutters_present ) ) {
2020-09-09 14:24:21 +01:00
if ( XdrvMailbox . data_len > 0 ) {
2020-09-09 15:23:50 +01:00
uint8_t i = 0 ;
2020-09-09 14:24:21 +01:00
char * str_ptr ;
char data_copy [ strlen ( XdrvMailbox . data ) + 1 ] ;
strncpy ( data_copy , XdrvMailbox . data , sizeof ( data_copy ) ) ; // Duplicate data as strtok_r will modify it.
// Loop through the data string, splitting on ' ' seperators.
2023-11-03 15:01:57 +00:00
for ( char * str = strtok_r ( data_copy , " , " , & str_ptr ) ; str & & i < 2 ; str = strtok_r ( nullptr , " , " , & str_ptr ) , i + + ) {
2020-09-09 15:23:50 +01:00
uint16_t field = atoi ( str ) ;
2020-09-09 14:24:21 +01:00
// The fields in a data string can only range from 1-30000.
// and following value must be higher than previous one
if ( ( field < = 0 ) | | ( field > 1023 ) ) {
break ;
}
2021-06-11 17:14:12 +01:00
Settings - > shutter_pwmrange [ i ] [ XdrvMailbox . index - 1 ] = field ;
2020-09-09 14:24:21 +01:00
}
2021-06-11 17:14:12 +01:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " SHT: Shtr%d Init1. pwmmin %d, pwmmax %d " ) , XdrvMailbox . index , Settings - > shutter_pwmrange [ 0 ] [ XdrvMailbox . index - 1 ] , Settings - > shutter_pwmrange [ 1 ] [ XdrvMailbox . index - 1 ] ) ;
2020-09-09 14:24:21 +01:00
ShutterInit ( ) ;
ResponseCmndIdxChar ( XdrvMailbox . data ) ;
} else {
char setting_chr [ 30 ] = " 0 " ;
2021-06-11 17:14:12 +01:00
snprintf_P ( setting_chr , sizeof ( setting_chr ) , PSTR ( " Shutter %d: min:%d max:%d " ) , XdrvMailbox . index , Settings - > shutter_pwmrange [ 0 ] [ XdrvMailbox . index - 1 ] , Settings - > shutter_pwmrange [ 1 ] [ XdrvMailbox . index - 1 ] ) ;
2020-09-09 14:24:21 +01:00
ResponseCmndIdxChar ( setting_chr ) ;
}
}
}
2020-01-09 10:35:01 +00:00
void CmndShutterCalibration ( void )
2019-09-29 17:00:01 +01:00
{
2020-10-30 11:29:48 +00:00
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = TasmotaGlobal . shutters_present ) ) {
2019-09-29 17:00:01 +01:00
if ( XdrvMailbox . data_len > 0 ) {
2020-09-09 15:23:50 +01:00
uint8_t i = 0 ;
2020-01-09 10:35:01 +00:00
char * str_ptr ;
char data_copy [ strlen ( XdrvMailbox . data ) + 1 ] ;
strncpy ( data_copy , XdrvMailbox . data , sizeof ( data_copy ) ) ; // Duplicate data as strtok_r will modify it.
// Loop through the data string, splitting on ' ' seperators.
2023-11-03 15:01:57 +00:00
for ( char * str = strtok_r ( data_copy , " , " , & str_ptr ) ; str & & i < 5 ; str = strtok_r ( nullptr , " , " , & str_ptr ) , i + + ) {
2020-01-09 10:35:01 +00:00
int field = atoi ( str ) ;
// The fields in a data string can only range from 1-30000.
// and following value must be higher than previous one
if ( ( field < = 0 ) | | ( field > 30000 ) | | ( ( i > 0 ) & & ( field < = messwerte [ i - 1 ] ) ) ) {
break ;
2019-10-09 05:36:11 +01:00
}
2020-01-09 10:35:01 +00:00
messwerte [ i ] = field ;
}
2021-06-11 17:14:12 +01:00
Settings - > shutter_set50percent [ XdrvMailbox . index - 1 ] = 50 ;
2020-01-09 10:35:01 +00:00
for ( i = 0 ; i < 5 ; i + + ) {
2021-06-11 17:14:12 +01:00
Settings - > shuttercoeff [ i ] [ XdrvMailbox . index - 1 ] = SHT_DIV_ROUND ( ( uint32_t ) messwerte [ i ] * 1000 , messwerte [ 4 ] ) ;
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " SHT: Shuttercoeff %d, i %d, Value %d, MeasuredValue %d " ) , i , XdrvMailbox . index - 1 , Settings - > shuttercoeff [ i ] [ XdrvMailbox . index - 1 ] , messwerte [ i ] ) ;
2020-01-09 10:35:01 +00:00
}
ShutterInit ( ) ;
ResponseCmndIdxChar ( XdrvMailbox . data ) ;
2020-01-02 11:37:07 +00:00
} else {
char setting_chr [ 30 ] = " 0 " ;
2021-06-11 17:14:12 +01:00
snprintf_P ( setting_chr , sizeof ( setting_chr ) , PSTR ( " %d %d %d %d %d " ) , Settings - > shuttercoeff [ 0 ] [ XdrvMailbox . index - 1 ] , Settings - > shuttercoeff [ 1 ] [ XdrvMailbox . index - 1 ] , Settings - > shuttercoeff [ 2 ] [ XdrvMailbox . index - 1 ] , Settings - > shuttercoeff [ 3 ] [ XdrvMailbox . index - 1 ] , Settings - > shuttercoeff [ 4 ] [ XdrvMailbox . index - 1 ] ) ;
2020-01-02 11:37:07 +00:00
ResponseCmndIdxChar ( setting_chr ) ;
2019-09-29 17:00:01 +01:00
}
}
}
2021-11-16 12:02:36 +00:00
void ShutterOptionsSetHelper ( uint16_t option )
{
2020-10-30 11:29:48 +00:00
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = TasmotaGlobal . shutters_present ) ) {
2020-01-12 13:18:15 +00:00
if ( XdrvMailbox . payload = = 0 ) {
2021-06-11 17:14:12 +01:00
Settings - > shutter_options [ XdrvMailbox . index - 1 ] & = ~ ( option ) ;
2020-01-12 13:18:15 +00:00
} else if ( XdrvMailbox . payload = = 1 ) {
2021-06-11 17:14:12 +01:00
Settings - > shutter_options [ XdrvMailbox . index - 1 ] | = ( option ) ;
2020-01-12 13:18:15 +00:00
}
2021-06-11 17:14:12 +01:00
ResponseCmndIdxNumber ( ( Settings - > shutter_options [ XdrvMailbox . index - 1 ] & option ) ? 1 : 0 ) ;
2020-01-12 13:18:15 +00:00
}
}
2021-11-16 12:02:36 +00:00
void CmndShutterInvert ( void )
{
2020-09-09 13:04:57 +01:00
ShutterOptionsSetHelper ( 1 ) ;
}
2021-11-16 12:02:36 +00:00
void CmndShutterLock ( void )
{
2020-09-09 13:04:57 +01:00
ShutterOptionsSetHelper ( 2 ) ;
}
2021-11-16 12:02:36 +00:00
void CmndShutterEnableEndStopTime ( void )
{
2020-09-09 13:04:57 +01:00
ShutterOptionsSetHelper ( 4 ) ;
2020-01-13 11:00:34 +00:00
}
2021-11-16 12:02:36 +00:00
void CmndShutterInvertWebButtons ( void )
{
2020-09-09 13:04:57 +01:00
ShutterOptionsSetHelper ( 8 ) ;
2020-03-10 07:41:37 +00:00
}
2021-11-16 12:02:36 +00:00
void CmndShutterSetTilt ( void )
{
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = TasmotaGlobal . shutters_present ) ) {
if ( XdrvMailbox . payload ! = - 99 ) {
Shutter [ XdrvMailbox . index - 1 ] . tilt_target_pos = tmin ( tmax ( XdrvMailbox . payload , Shutter [ XdrvMailbox . index - 1 ] . tilt_config [ 0 ] ) , Shutter [ XdrvMailbox . index - 1 ] . tilt_config [ 1 ] ) ;
}
2022-12-10 22:52:53 +00:00
// assuming OPEN=100=tilt_config[3]/CLOSE=0=tilt_config[4]
if ( XdrvMailbox . data_len > 3 & & XdrvMailbox . payload > = 0 ) {
Shutter [ XdrvMailbox . index - 1 ] . tilt_target_pos = Shutter [ XdrvMailbox . index - 1 ] . tilt_config [ XdrvMailbox . payload ? 3 : 4 ] ;
2021-11-16 12:02:36 +00:00
}
}
XdrvMailbox . data [ 0 ] = ' \0 ' ;
2022-02-04 07:58:42 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " SHT: TiltTarget %d, payload %d " ) , Shutter [ XdrvMailbox . index - 1 ] . tilt_target_pos , XdrvMailbox . payload ) ;
2021-11-16 12:02:36 +00:00
Shutter [ XdrvMailbox . index - 1 ] . tiltmoving = 1 ;
2022-12-10 22:52:53 +00:00
// Avoid shutterposition try to interpret "open/close or payload"
XdrvMailbox . data_len = 0 ;
XdrvMailbox . payload = - 99 ;
2021-11-16 12:02:36 +00:00
CmndShutterPosition ( ) ;
}
void CmndShutterTiltConfig ( void )
{
2021-11-07 14:53:12 +00:00
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = TasmotaGlobal . shutters_present ) ) {
if ( XdrvMailbox . data_len > 0 ) {
uint8_t i = 0 ;
char * str_ptr ;
char data_copy [ strlen ( XdrvMailbox . data ) + 1 ] ;
strncpy ( data_copy , XdrvMailbox . data , sizeof ( data_copy ) ) ; // Duplicate data as strtok_r will modify it.
// Loop through the data string, splitting on ' ' seperators.
2023-11-03 15:01:57 +00:00
for ( char * str = strtok_r ( data_copy , " , " , & str_ptr ) ; str & & i < 6 ; str = strtok_r ( nullptr , " , " , & str_ptr ) , i + + ) {
2021-11-07 14:53:12 +00:00
Shutter [ XdrvMailbox . index - 1 ] . tilt_config [ i ] = Settings - > shutter_tilt_config [ i ] [ XdrvMailbox . index - 1 ] = atoi ( str ) ;
}
2022-05-13 18:11:00 +01:00
// avoid negative runtime
Settings - > shutter_tilt_config [ 2 ] [ XdrvMailbox . index - 1 ] = Shutter [ XdrvMailbox . index - 1 ] . tilt_config [ 2 ] = Shutter [ XdrvMailbox . index - 1 ] . tilt_config [ 2 ] > = 0 ? Shutter [ XdrvMailbox . index - 1 ] . tilt_config [ 2 ] : 127 ;
2021-11-07 14:53:12 +00:00
ShutterInit ( ) ;
}
2022-05-13 18:11:00 +01:00
char setting_chr [ 30 ] = " 0 " ;
2022-12-19 19:31:02 +00:00
snprintf_P ( setting_chr , sizeof ( setting_chr ) , PSTR ( " %d %d %d %d %d " ) , Shutter [ XdrvMailbox . index - 1 ] . tilt_config [ 0 ] , Shutter [ XdrvMailbox . index - 1 ] . tilt_config [ 1 ] , Shutter [ XdrvMailbox . index - 1 ] . tilt_config [ 2 ] , Shutter [ XdrvMailbox . index - 1 ] . tilt_config [ 3 ] , Shutter [ XdrvMailbox . index - 1 ] . tilt_config [ 4 ] ) ;
2022-05-13 18:11:00 +01:00
ResponseCmndIdxChar ( setting_chr ) ;
AddLog ( LOG_LEVEL_INFO , PSTR ( " SHT: TiltConfig %d, min: %d, max %d, runtime %d, close_pos: %d, open_pos: %d " ) , XdrvMailbox . index , Shutter [ XdrvMailbox . index - 1 ] . tilt_config [ 0 ] , Shutter [ XdrvMailbox . index - 1 ] . tilt_config [ 1 ] , Shutter [ XdrvMailbox . index - 1 ] . tilt_config [ 2 ] , Shutter [ XdrvMailbox . index - 1 ] . tilt_config [ 3 ] , Shutter [ XdrvMailbox . index - 1 ] . tilt_config [ 4 ] ) ;
2021-11-07 14:53:12 +00:00
}
}
2021-11-16 12:02:36 +00:00
void CmndShutterTiltIncDec ( void )
{
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Change in: payload %s (%d), payload %d, idx %d, src %d"), XdrvMailbox.data , XdrvMailbox.data_len, XdrvMailbox.payload , XdrvMailbox.index, TasmotaGlobal.last_source );
2022-05-13 18:11:00 +01:00
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = TasmotaGlobal . shutters_present ) & & XdrvMailbox . data_len > 0 ) {
XdrvMailbox . payload = Shutter [ XdrvMailbox . index - 1 ] . tilt_target_pos + XdrvMailbox . payload ;
CmndShutterSetTilt ( ) ;
} else {
ResponseCmndIdxNumber ( XdrvMailbox . payload ) ;
2021-11-16 12:02:36 +00:00
}
}
2022-12-14 10:25:41 +00:00
void CmndShutterMotorStop ( void )
{
if ( ! XdrvMailbox . usridx ) {
if ( ( XdrvMailbox . payload > = 0 ) ) {
Settings - > shutter_motorstop = XdrvMailbox . payload ;
2022-12-22 16:02:01 +00:00
ShutterInit ( ) ;
2022-12-14 10:25:41 +00:00
}
ResponseCmndNumber ( Settings - > shutter_motorstop ) ;
}
}
2019-09-29 17:00:01 +01:00
/*********************************************************************************************\
* Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2022-11-11 09:44:56 +00:00
bool Xdrv27 ( uint32_t function )
2019-09-29 17:00:01 +01:00
{
bool result = false ;
2021-06-11 17:14:12 +01:00
if ( Settings - > flag3 . shutter_mode ) { // SetOption80 - Enable shutter support
2022-12-14 10:25:41 +00:00
uint8_t counter = XdrvMailbox . index = = 0 ? 1 : XdrvMailbox . index ;
uint8_t counterend = XdrvMailbox . index = = 0 ? TasmotaGlobal . shutters_present : XdrvMailbox . index ;
int32_t rescue_payload = XdrvMailbox . payload ;
uint32_t rescue_data_len = XdrvMailbox . data_len ;
2023-04-04 15:17:12 +01:00
char stemp1 [ 10 ] ;
power_t save_powermatrix ;
2019-09-29 17:00:01 +01:00
switch ( function ) {
case FUNC_PRE_INIT :
ShutterInit ( ) ;
break ;
2021-11-16 20:11:34 +00:00
case FUNC_EVERY_50_MSECOND :
2019-09-30 10:21:43 +01:00
ShutterUpdatePosition ( ) ;
2019-09-29 17:00:01 +01:00
break ;
case FUNC_EVERY_SECOND :
2019-12-14 11:09:35 +00:00
//case FUNC_EVERY_250_MSECOND:
2020-04-21 15:19:42 +01:00
ShutterReportPosition ( false , MAX_SHUTTERS ) ;
2019-09-29 17:00:01 +01:00
break ;
case FUNC_COMMAND :
2022-12-14 10:25:41 +00:00
for ( uint8_t i = counter ; i < = counterend ; i + + ) {
XdrvMailbox . index = i ;
XdrvMailbox . payload = rescue_payload ;
XdrvMailbox . data_len = rescue_data_len ;
result = DecodeCommand ( kShutterCommands , ShutterCommand ) ;
}
2019-09-29 17:00:01 +01:00
break ;
2023-04-26 07:39:49 +01:00
for ( uint8_t i = counter ; i < = counterend ; i + + ) {
XdrvMailbox . index = i ;
XdrvMailbox . payload = rescue_payload ;
XdrvMailbox . data_len = rescue_data_len ;
result = DecodeCommand ( kShutterCommands , ShutterCommand ) ;
}
break ;
2019-09-29 17:00:01 +01:00
case FUNC_JSON_APPEND :
2020-10-30 11:29:48 +00:00
for ( uint8_t i = 0 ; i < TasmotaGlobal . shutters_present ; i + + ) {
2023-09-29 16:46:41 +01:00
uint8_t position = ShutterRealToPercentPosition ( Shutter [ i ] . real_position , i ) ;
uint8_t target = ShutterRealToPercentPosition ( Shutter [ i ] . target_position , i ) ;
2019-09-29 17:00:01 +01:00
ResponseAppend_P ( " , " ) ;
2023-09-29 16:46:41 +01:00
ResponseAppend_P ( JSON_SHUTTER_POS , i + 1 , ( Settings - > shutter_options [ i ] & 1 ) ? 100 - position : position , Shutter [ i ] . direction , ( Settings - > shutter_options [ i ] & 1 ) ? 100 - target : target , Shutter [ i ] . tilt_real_pos ) ;
2019-09-29 17:00:01 +01:00
# ifdef USE_DOMOTICZ
2020-10-29 12:37:09 +00:00
if ( ( 0 = = TasmotaGlobal . tele_period ) & & ( 0 = = i ) ) {
2019-09-29 17:00:01 +01:00
DomoticzSensor ( DZ_SHUTTER , position ) ;
}
# endif // USE_DOMOTICZ
}
break ;
case FUNC_SET_POWER :
2023-04-04 15:17:12 +01:00
2019-09-29 17:00:01 +01:00
// extract the number of the relay that was switched and save for later in Update Position.
2020-09-09 13:04:57 +01:00
ShutterGlobal . RelayCurrentMask = XdrvMailbox . index ^ ShutterGlobal . RelayOldMask ;
2023-04-04 15:17:12 +01:00
ShutterGlobal . LastChangedRelay = ShutterGetRelayNoFromBitfield ( XdrvMailbox . index ^ ShutterGlobal . RelayOldMask ) ;
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: FUNC_SET_POWER Relaymask %d SwitchedRelay:%d by %s, payload %d, powermask %d " ) , ShutterGlobal . RelayOldMask , ShutterGlobal . LastChangedRelay , GetTextIndexed ( stemp1 , sizeof ( stemp1 ) , TasmotaGlobal . last_source , kCommandSource ) , XdrvMailbox . payload , TasmotaGlobal . power ) ;
save_powermatrix = TasmotaGlobal . power ;
if ( ! ShutterGlobal . LastChangedRelay ) {
ShutterGlobal . skip_relay_change = 1 ;
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("INVALID REQUEST"));
} else {
ShutterRelayChanged ( ) ;
ShutterGlobal . RelayOldMask = XdrvMailbox . index ;
TasmotaGlobal . power = save_powermatrix ;
}
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: FUNC_SET_POWER end. powermask %d " ) , TasmotaGlobal . power ) ;
2019-12-12 15:14:06 +00:00
break ;
case FUNC_SET_DEVICE_POWER :
2020-09-09 13:04:57 +01:00
if ( ShutterGlobal . skip_relay_change ) {
2021-01-23 15:26:23 +00:00
//AddLog(LOG_LEVEL_ERROR, PSTR("SHT: Skip relay change %d"), i+1);
2019-12-12 15:14:06 +00:00
result = true ;
2020-09-09 13:04:57 +01:00
ShutterGlobal . skip_relay_change = 0 ;
2023-04-04 15:17:12 +01:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: Skipping switch off relay %d " ) , ShutterGlobal . LastChangedRelay ) ;
//ExecuteCommandPowerShutter(i+1, 0, SRC_SHUTTER);
if ( ShutterGlobal . LastChangedRelay ) ShutterGlobal . RelayOldMask = TasmotaGlobal . power ^ = 1 < < ( ShutterGlobal . LastChangedRelay - 1 ) ;
//ShutterGlobal.RelayOldMask ^= 1<<(ShutterGlobal.LastChangedRelay-1);
2019-12-12 15:14:06 +00:00
}
2019-09-29 17:00:01 +01:00
break ;
2020-01-02 10:23:11 +00:00
case FUNC_BUTTON_PRESSED :
2022-08-03 18:03:42 +01:00
if ( XdrvMailbox . index < MAX_SHUTTER_KEYS & & Settings - > shutter_button [ XdrvMailbox . index ] & ( 1 < < 31 ) ) {
2020-01-02 10:23:11 +00:00
ShutterButtonHandler ( ) ;
result = true ;
}
break ;
2023-05-21 11:25:11 +01:00
# ifdef USE_WEBSERVER
case FUNC_WEB_SENSOR :
ShutterShow ( ) ;
break ;
# endif // USE_WEBSERVER
2023-12-27 21:03:56 +00:00
case FUNC_ACTIVE :
result = true ;
break ;
2019-09-29 17:00:01 +01:00
}
}
return result ;
}
# endif //USE_SHUTTER
2021-02-18 11:42:59 +00:00
# ifdef SHUTTER_UNITTEST
void CmndShutterUnitTest ( void ) {
int16_t input_percent [ 10 ] = { - 5 , 0 , 10 , 26 , 35 , 55 , 80 , 99 , 100 , 105 } ;
int16_t output_percent [ 10 ] = { 0 , 0 , 10 , 26 , 35 , 55 , 80 , 99 , 100 , 100 } ;
uint32_t result_percent [ 2 ] [ 2 ] [ 10 ] = { { { 0 , 0 , 24000 , 62400 , 84000 , 132000 , 192000 , 237600 , 240000 , 240000 } ,
{ 0 , 0 , 360000 , 936000 , 1260000 , 1980000 , 2880000 , 3564000 , 3600000 , 3600000 } } ,
{ { 0 , 0 , 76296 , 100000 , 113333 , 174299 , 205795 , 237983 , 240000 , 240000 } ,
{ 0 , 0 , 1144444 , 1500000 , 1700000 , 2614488 , 3086929 , 3569748 , 3600000 , 3600000 } } } ;
uint32_t result = 0 ;
char svalue [ 50 ] ; // Command and number parameter
2021-06-11 17:14:12 +01:00
Settings - > shuttercoeff [ 0 ] [ 0 ] = 0 ;
2021-02-18 11:42:59 +00:00
for ( uint8_t i = 0 ; i < 2 ; i + + ) {
snprintf_P ( svalue , sizeof ( svalue ) , PSTR ( D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME " %d %d " ) , 1 , 12 ) ;
ExecuteCommand ( svalue , SRC_SHUTTER ) ;
ShutterInit ( ) ;
for ( uint8_t j = 0 ; j < 2 ; j + + ) {
for ( uint8_t k = 0 ; k < 10 ; k + + ) {
result + = ( result_percent [ i ] [ j ] [ k ] = = ShutterPercentToRealPosition ( input_percent [ k ] , 0 ) ? 0 : 1 ) ;
AddLog ( LOG_LEVEL_ERROR , PSTR ( " SHT: ShutterPercentToRealPosition error %d: %d <-> %d " ) , result , ShutterPercentToRealPosition ( input_percent [ k ] , 0 ) , result_percent [ i ] [ j ] [ k ] ) ;
}
snprintf_P ( svalue , sizeof ( svalue ) , PSTR ( D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME " %d %d " ) , 1 , 180 ) ;
ExecuteCommand ( svalue , SRC_SHUTTER ) ;
}
snprintf_P ( svalue , sizeof ( svalue ) , PSTR ( D_PRFX_SHUTTER D_CMND_SHUTTER_CLIBRATION " %d %s " ) , 1 , " 15 83 105 185 210 " ) ;
ExecuteCommand ( svalue , SRC_SHUTTER ) ;
}
if ( ! result ) {
AddLog ( LOG_LEVEL_ERROR , PSTR ( " SHT: ShutterPercentToRealPosition: PASS " ) ) ;
} else {
AddLog ( LOG_LEVEL_ERROR , PSTR ( " SHT: ShutterPercentToRealPosition: FAIL " ) ) ;
}
2021-06-11 17:14:12 +01:00
Settings - > shuttercoeff [ 0 ] [ 0 ] = 0 ;
2021-02-18 11:42:59 +00:00
for ( uint8_t i = 0 ; i < 2 ; i + + ) {
snprintf_P ( svalue , sizeof ( svalue ) , PSTR ( D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME " %d %d " ) , 1 , 12 ) ;
ExecuteCommand ( svalue , SRC_SHUTTER ) ;
ShutterInit ( ) ;
for ( uint8_t j = 0 ; j < 2 ; j + + ) {
for ( uint8_t k = 0 ; k < 10 ; k + + ) {
result + = ( output_percent [ k ] = = ShutterRealToPercentPosition ( result_percent [ i ] [ j ] [ k ] , 0 ) ? 0 : 1 ) ;
AddLog ( LOG_LEVEL_ERROR , PSTR ( " SHT: ShutterRealToPercentPosition error %d: %d <-> %d " ) , result , ShutterRealToPercentPosition ( result_percent [ i ] [ j ] [ k ] , 0 ) , output_percent [ k ] ) ;
}
snprintf_P ( svalue , sizeof ( svalue ) , PSTR ( D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME " %d %d " ) , 1 , 180 ) ;
ExecuteCommand ( svalue , SRC_SHUTTER ) ;
}
snprintf_P ( svalue , sizeof ( svalue ) , PSTR ( D_PRFX_SHUTTER D_CMND_SHUTTER_CLIBRATION " %d %s " ) , 1 , " 15 83 105 185 210 " ) ;
ExecuteCommand ( svalue , SRC_SHUTTER ) ;
}
if ( ! result ) {
AddLog ( LOG_LEVEL_ERROR , PSTR ( " SHT: ShutterRealToPercentPosition: PASS " ) ) ;
} else {
AddLog ( LOG_LEVEL_ERROR , PSTR ( " SHT: ShutterRealToPercentPosition: FAIL " ) ) ;
}
}
# else
void CmndShutterUnitTest ( void ) { }
# endif
2023-03-29 14:43:19 +01:00
# endif // ESP8266