2019-09-29 17:00:01 +01:00
/*
2020-09-09 13:04:57 +01:00
xdrv_27_Shutter [ i ] . ino - Shutter / Blind support for Tasmota
2019-09-29 17:00:01 +01:00
2021-01-01 12:44:04 +00:00
Copyright ( C ) 2021 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/>.
*/
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
2021-02-18 11:42:59 +00:00
//#define SHUTTER_UNITTEST
2019-09-29 17:00:01 +01:00
# define D_SHUTTER "SHUTTER"
2019-12-04 11:34:27 +00:00
const uint16_t MOTOR_STOP_TIME = 500 ; // in mS
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
2020-09-05 19:39:24 +01:00
const uint16_t pwm_max = 500 ;
const uint16_t pwm_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 ;
2020-09-08 18:34:10 +01:00
int32_t toBeAcc = 0 ;
2019-10-09 05:36:11 +01:00
2020-09-05 19:39:24 +01:00
const uint8_t MAX_MODES = 7 ;
2020-09-09 07:58:00 +01:00
enum Shutterposition_mode { SHT_UNDEF , SHT_TIME , SHT_TIME_UP_DOWN , SHT_TIME_GARAGE , SHT_COUNTER , SHT_PWM_VALUE , SHT_PWM_TIME , } ;
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 " | "
D_CMND_SHUTTER_UNITTEST " | " ;
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 ,
& CmndShutterUnitTest } ;
2019-09-29 17:00:01 +01:00
2020-02-24 11:23:03 +00:00
const char JSON_SHUTTER_POS [ ] PROGMEM = " \" " D_PRFX_SHUTTER " %d \" :{ \" Position \" :%d, \" Direction \" :%d, \" Target \" :%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
int16_t motordelay ; // initial motorstarttime in 0.05sec. Also uses for ramp at steppers and servos
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
} 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
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
2020-09-09 07:58:00 +01:00
uint16_t open_velocity_max = 1000 ; // 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))
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-02-18 11:42:59 +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 " ) ,
2020-09-09 13:04:57 +01:00
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 ) ;
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.
2020-09-09 13:04:57 +01:00
Shutter [ i ] . pwm_velocity + = Shutter [ i ] . accelerator ;
Shutter [ i ] . pwm_velocity = tmax ( 0 , tmin ( Shutter [ i ] . direction = = 1 ? ShutterGlobal . open_velocity_max : Shutter [ i ] . close_velocity_max , Shutter [ i ] . pwm_velocity ) ) ;
2021-02-18 11:42:59 +00:00
if ( ShutterGlobal . position_mode = = SHT_COUNTER ) {
Shutter [ i ] . pwm_velocity = tmax ( 100 , Shutter [ i ] . pwm_velocity ) ;
}
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
{
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 ) ;
2020-09-09 14:24:21 +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 ] ;
2020-09-09 13:04:57 +01: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 ) ;
2020-09-09 13:04:57 +01:00
analogWriteFreq ( Shutter [ i ] . pwm_velocity ) ;
2020-09-08 18:34:10 +01:00
analogWrite ( Pin ( GPIO_PWM1 , i ) , 50 ) ;
}
break ;
}
2020-09-09 13:04:57 +01:00
} // if (Shutter[i].direction)
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
{
2019-12-28 16:24:52 +00:00
if ( Settings . shutter_set50percent [ index ] ! = 50 ) {
2020-10-05 11:45:08 +01:00
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 + + ) {
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 + + ) {
2020-01-13 08:48:29 +00: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 + + ) {
if ( ( percent * 10 ) > = Settings . shuttercoeff [ k ] [ index ] ) {
realpos = SHT_DIV_ROUND ( Shutter [ index ] . open_max * calibrate_pos [ k + 1 ] , 100 ) ;
2021-02-18 11:42:59 +00: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 ) {
2020-12-28 11:27:45 +00:00
realpos = SHT_DIV_ROUND ( ( int64_t ) percent * Shutter [ index ] . open_max * calibrate_pos [ k + 1 ] , Settings . shuttercoeff [ k ] [ index ] * 10 ) ;
2021-02-18 11:42:59 +00:00
//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 {
2020-12-15 16:44:06 +00: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;
2021-02-18 11:42:59 +00:00
//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]));
2020-12-28 11:27:45 +00:00
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
{
2019-12-28 16:24:52 +00:00
if ( Settings . shutter_set50percent [ index ] ! = 50 ) {
2020-10-07 17:01:07 +01:00
return ( 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-12-28 11:27:45 +00:00
int64_t realpercent ;
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 ) {
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 ) {
2020-12-28 11:27:45 +00: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 {
2020-12-15 16:44:06 +00: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;
2021-02-18 11:42:59 +00:00
//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]));
2020-12-28 11:27:45 +00:00
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 ) ;
}
2019-10-09 05:36:11 +01:00
break ;
}
}
2020-12-28 11:27:45 +00:00
return realpercent < 0 ? 0 : realpercent ;
2019-10-09 05:36:11 +01:00
}
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
2020-06-30 14:05:07 +01:00
if ( Settings . shutter_startrelay [ MAX_SHUTTERS - 1 ] = = 0 ) {
2020-09-09 13:04:57 +01:00
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
Settings . shutter_startrelay [ i ] = ( Settings . shutter_startrelay [ i ] = = 0 & & i = = 0 ? 1 : Settings . shutter_startrelay [ i ] ) ;
2020-01-04 14:09:57 +00:00
if ( Settings . shutter_startrelay [ i ] & & ( Settings . shutter_startrelay [ i ] < 9 ) ) {
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
2020-09-09 13:04:57 +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
2020-09-05 19:39:24 +01:00
switch ( Settings . pulse_timer [ i ] ) {
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 ;
}
if ( Settings . shutter_mode = = SHT_UNDEF ) {
2020-09-09 07:58:00 +01:00
bool relay_in_interlock = false ;
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: Mode undef.. calculate... " ) ) ;
2020-09-09 07:58:00 +01:00
for ( uint32_t j = 0 ; j < MAX_INTERLOCKS * Settings . flag . interlock ; j + + ) { // CMND_INTERLOCK - Enable/disable interlock
2021-02-18 11:42:59 +00:00
//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);
2020-09-09 13:04:57 +01:00
if ( Settings . interlock [ j ] & & ( Settings . interlock [ j ] & ShutterGlobal . RelayShutterMask ) ) {
2020-11-06 16:09:13 +00:00
//AddLog_P(LOG_LEVEL_DEBUG, PSTR("SHT: Relay in Interlock group"));
2020-09-09 07:58:00 +01:00
relay_in_interlock = true ;
}
}
2020-09-09 13:04:57 +01:00
if ( relay_in_interlock ) {
ShutterGlobal . position_mode = SHT_TIME ;
} else {
ShutterGlobal . position_mode = SHT_TIME_UP_DOWN ;
if ( PinUsed ( GPIO_PWM1 , i ) & & PinUsed ( GPIO_CNTR1 , i ) ) {
ShutterGlobal . position_mode = SHT_COUNTER ;
}
2019-09-29 17:00:01 +01:00
}
2020-09-09 13:04:57 +01:00
2019-09-29 17:00:01 +01:00
} else {
2020-09-09 13:04:57 +01:00
ShutterGlobal . position_mode = Settings . shutter_mode ;
2019-09-29 17:00:01 +01:00
}
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
2020-01-04 14:09:57 +00: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
2020-09-09 13:04:57 +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
2019-10-09 05:36:11 +01:00
if ( Settings . shutter_set50percent [ i ] ! = 50 ) {
2020-10-05 11:45:08 +01:00
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 ;
2021-02-18 11:42:59 +00:00
//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
}
2020-09-09 13:04:57 +01:00
ShutterGlobal . RelayShutterMask | = 3 < < ( Settings . shutter_startrelay [ i ] - 1 ) ;
2019-09-30 10:21:43 +01:00
2020-09-09 13:04:57 +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 ;
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
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.
Settings . shutter_pwmrange [ 0 ] [ i ] = Settings . shutter_pwmrange [ 0 ] [ i ] > 0 ? Settings . shutter_pwmrange [ 0 ] [ i ] : pwm_min ;
2020-09-09 15:34:03 +01:00
Settings . shutter_pwmrange [ 1 ] [ i ] = Settings . shutter_pwmrange [ 1 ] [ i ] > 0 ? Settings . shutter_pwmrange [ 1 ] [ i ] : pwm_max ;
2020-09-05 19:39:24 +01:00
break ;
}
2020-09-09 13:04:57 +01:00
Shutter [ i ] . close_velocity_max = ShutterGlobal . open_velocity_max * Shutter [ i ] . open_time / Shutter [ i ] . close_time ;
2020-09-05 19:39:24 +01:00
2021-02-18 11:42:59 +00:00
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shtr%d Openvel %d, Closevel: %d"),i, ShutterGlobal.open_velocity_max, Shutter[i].close_velocity_max);
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " SHT: Shtr%d Init. Pos %d, Inverted %d, Locked %d, End stop time enabled %d, webButtons inverted %d " ) ,
2020-09-09 13:04:57 +01:00
i + 1 , Shutter [ i ] . real_position ,
2020-09-05 19:39:24 +01: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 ) ;
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 ) ;
2019-09-29 17:00:01 +01:00
Settings . shutter_accuracy = 1 ;
}
}
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-10-29 12:58:50 +00:00
TasmotaGlobal . rules_flag . shutter_moving = 0 ;
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 ;
2020-04-21 15:19:42 +01:00
if ( index ! = MAX_SHUTTERS ) {
i = index ;
n = index + 1 ;
}
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);
2020-09-09 13:04:57 +01:00
uint32_t position = ShutterRealToPercentPosition ( Shutter [ i ] . real_position , i ) ;
if ( Shutter [ i ] . direction ! = 0 ) {
2020-10-29 12:58:50 +00:00
TasmotaGlobal . rules_flag . shutter_moving = 1 ;
2020-01-04 14:09:57 +00:00
ShutterLogPos ( i ) ;
2020-01-04 01:36:05 +00:00
}
2020-04-21 15:19:42 +01:00
if ( i & & index = = MAX_SHUTTERS ) { ResponseAppend_P ( PSTR ( " , " ) ) ; }
2020-09-09 13:04:57 +01:00
uint32_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 ) ;
2020-01-04 01:36:05 +00:00
}
ResponseJsonEnd ( ) ;
2020-10-29 12:58:50 +00:00
if ( always | | ( TasmotaGlobal . rules_flag . shutter_moving ) ) {
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
}
2020-01-13 11:00:34 +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 :
// 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
2020-09-09 13:04:57 +01:00
min_runtime_ms = Shutter [ i ] . pwm_velocity * 1000 / STEPS_PER_SECOND / velocity_change_per_step_max ;
2020-09-08 18:34:10 +01:00
// decellartion way from current velocity
2020-09-09 13:04:57 +01:00
current_stop_way = ( min_runtime_ms * ( Shutter [ i ] . pwm_velocity + velocity_change_per_step_max ) / 100 - Shutter [ i ] . pwm_velocity ) * RESOLUTION / ShutterGlobal . open_velocity_max * Shutter [ i ] . direction ;
next_possible_stop_position = Shutter [ i ] . real_position + current_stop_way ;
2020-09-08 18:34:10 +01:00
toBeAcc = 0 ;
// ensure that the accelerotor kicks in at least one step BEFORE it is to late and a hard stop required.
2020-09-09 13:04:57 +01:00
if ( Shutter [ i ] . accelerator < 0 | | ( next_possible_stop_position * Shutter [ i ] . direction ) + RESOLUTION * Shutter [ i ] . pwm_velocity / ShutterGlobal . open_velocity_max > = Shutter [ i ] . target_position * Shutter [ i ] . direction ) {
2020-09-09 07:58:00 +01:00
// 10 times the deviation is the p-value of this simple p-regulator
2020-09-09 13:04:57 +01:00
toBeAcc = 100 + ( Shutter [ i ] . direction * ( next_possible_stop_position - Shutter [ i ] . target_position ) * velocity_max / Shutter [ i ] . pwm_velocity * 10 / RESOLUTION ) ;
Shutter [ i ] . accelerator = - tmin ( tmax ( velocity_change_per_step_max * toBeAcc / 100 , ( velocity_change_per_step_max * 9 / 10 ) ) , ( velocity_change_per_step_max * 11 / 10 ) ) ;
} else if ( Shutter [ i ] . accelerator > 0 & & Shutter [ i ] . pwm_velocity = = velocity_max ) {
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 )
{
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-20 14:11:52 +00:00
while ( Shutter [ i ] . pwm_velocity > - 2 * Shutter [ i ] . accelerator | | ( ShutterGlobal . position_mode = = SHT_COUNTER & & Shutter [ i ] . pwm_velocity > 100 ) ) {
2021-02-18 11:42:59 +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.
2021-02-18 11:42:59 +00:00
2020-09-05 19:39:24 +01:00
}
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 ;
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 ) {
2020-09-05 19:39:24 +01:00
}
analogWrite ( Pin ( GPIO_PWM1 , i ) , 0 ) ; // removed with 8.3 because of reset caused by watchog
2020-09-09 13:04:57 +01:00
Shutter [ i ] . real_position = ShutterCalculatePosition ( i ) ;
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: Real %d, Pulsecount %d, Start %d " ) , Shutter [ i ] . real_position , RtcSettings . pulse_counter [ i ] , 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 ;
}
}
void ShutterPowerOff ( uint8_t i ) {
2021-02-09 17:55:13 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " SHT: Stop Shutter %d. Switchmode %d " ) , i + 1 , Shutter [ i ] . switch_mode ) ; // fix log to indicate correct shutter number
2020-09-05 19:39:24 +01:00
ShutterDecellerateForStop ( i ) ;
2020-09-09 13:04:57 +01:00
switch ( Shutter [ i ] . switch_mode ) {
2020-09-05 19:39:24 +01:00
case SHT_SWITCH :
2020-10-28 18:03:39 +00:00
if ( ( 1 < < ( Settings . shutter_startrelay [ i ] - 1 ) ) & TasmotaGlobal . power ) {
2020-09-05 19:39:24 +01:00
ExecuteCommandPowerShutter ( Settings . shutter_startrelay [ i ] , 0 , SRC_SHUTTER ) ;
}
2020-10-28 18:03:39 +00:00
if ( ( 1 < < ( Settings . shutter_startrelay [ i ] ) ) & TasmotaGlobal . power ) {
2020-09-05 19:39:24 +01:00
ExecuteCommandPowerShutter ( Settings . shutter_startrelay [ i ] + 1 , 0 , SRC_SHUTTER ) ;
}
break ;
case SHT_PULSE :
2020-09-09 13:04:57 +01:00
uint8_t cur_relay = Settings . shutter_startrelay [ i ] + ( Shutter [ i ] . direction = = 1 ? 0 : ( uint8_t ) ( ShutterGlobal . position_mode = = SHT_TIME ) ) ;
2020-09-05 19:39:24 +01:00
// 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
2020-12-15 11:44:49 +00:00
if ( ( ( 1 < < ( Settings . shutter_startrelay [ i ] ) ) & TasmotaGlobal . power ) & & Settings . shutter_startrelay [ i ] + 1 ! = cur_relay ) {
2020-09-05 19:39:24 +01:00
ExecuteCommandPowerShutter ( Settings . shutter_startrelay [ i ] + 1 , 0 , SRC_SHUTTER ) ;
}
} 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 :
char scmnd [ 20 ] ;
2021-02-09 22:51:38 +00:00
# ifdef SHUTTER_CLEAR_PWM_ONSTOP
// free the PWM servo lock on stop.
snprintf_P ( scmnd , sizeof ( scmnd ) , PSTR ( D_CMND_PWM " %d 0 " ) , i + 1 ) ;
# else
snprintf_P ( scmnd , sizeof ( scmnd ) , PSTR ( D_CMND_PWM " %d %d " ) , i + 1 , Shutter [ i ] . pwm_value ) ;
# endif
2020-09-08 18:34:10 +01:00
ExecuteCommand ( scmnd , SRC_BUTTON ) ;
break ;
}
2020-12-14 17:23:01 +00:00
if ( Shutter [ i ] . direction ! = 0 ) {
Shutter [ i ] . direction = 0 ;
delay ( MOTOR_STOP_TIME ) ;
}
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
{
2019-12-20 10:27:35 +00:00
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-04-21 15:18:18 +01:00
XdrvRulesProcess ( ) ;
2020-09-09 13:04:57 +01:00
ShutterGlobal . start_reported = 1 ;
2020-04-21 15:18:18 +01:00
}
2021-01-02 14:47:03 +00:00
AddLog_P ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: Time %d, toBA %d, cStop %d, cVelo %d, mVelo %d, aVelo %d, mRun %d, aPos %d, nStop %d, Trgt %d, mVelo %d, Dir %d " ) ,
Shutter [ i ] . time , toBeAcc , current_stop_way , Shutter [ i ] . pwm_velocity , velocity_max , Shutter [ i ] . accelerator , min_runtime_ms , Shutter [ i ] . real_position ,
next_possible_stop_position , Shutter [ i ] . target_position , velocity_change_per_step_max , Shutter [ i ] . direction ) ;
2019-12-20 10:27:35 +00:00
2021-02-20 14:06:36 +00:00
if ( Shutter [ i ] . real_position * Shutter [ i ] . direction > = Shutter [ i ] . target_position * Shutter [ i ] . direction | | ( ShutterGlobal . position_mode = = SHT_COUNTER & & Shutter [ i ] . accelerator < 0 & & Shutter [ i ] . pwm_velocity + Shutter [ i ] . accelerator < = 100 ) ) {
2020-09-09 13:04:57 +01:00
if ( Shutter [ i ] . direction ! = 0 ) {
Shutter [ i ] . lastdirection = Shutter [ i ] . direction ;
2019-09-29 17:00:01 +01:00
}
2020-09-05 19:39:24 +01:00
ShutterPowerOff ( i ) ;
2020-01-13 11:00:34 +00:00
ShutterLimitRealAndTargetPositions ( i ) ;
2020-09-09 13:04:57 +01:00
Settings . shutter_position [ i ] = ShutterRealToPercentPosition ( Shutter [ i ] . real_position , i ) ;
Shutter [ i ] . start_position = Shutter [ i ] . real_position ;
2019-12-02 20:44:05 +00:00
2020-01-04 14:09:57 +00:00
ShutterLogPos ( i ) ;
2019-12-02 20:44:05 +00:00
// sending MQTT result to broker
snprintf_P ( scommand , sizeof ( scommand ) , PSTR ( D_SHUTTER " %d " ) , i + 1 ) ;
2020-10-30 11:29:48 +00:00
GetTopic_P ( stopic , STAT , TasmotaGlobal . mqtt_topic , scommand ) ;
2020-01-12 13:18:15 +00:00
Response_P ( " %d " , ( Settings . shutter_options [ i ] & 1 ) ? 100 - Settings . shutter_position [ i ] : Settings . shutter_position [ i ] ) ;
2019-12-02 20:44:05 +00:00
MqttPublish ( stopic , Settings . flag . mqtt_power_retain ) ; // CMND_POWERRETAIN
2020-04-21 15:19:42 +01:00
ShutterReportPosition ( true , i ) ;
2020-10-29 12:58:50 +00:00
TasmotaGlobal . rules_flag . shutter_moved = 1 ;
2019-09-29 17:00:01 +01:00
XdrvRulesProcess ( ) ;
}
}
}
}
2020-01-04 14:09:57 +00:00
bool ShutterState ( uint32_t device )
2019-09-29 17:00:01 +01:00
{
device - - ;
device & = 3 ;
2019-11-03 12:51:22 +00:00
return ( Settings . flag3 . shutter_mode & & // SetOption80 - Enable shutter support
2020-09-09 13:04:57 +01:00
( ShutterGlobal . RelayShutterMask & ( 1 < < ( Settings . shutter_startrelay [ device ] - 1 ) ) ) ) ;
2019-09-29 17:00:01 +01: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
void ShutterAllowPreStartProcedure ( uint8_t i )
{
2020-10-11 09:51:20 +01:00
# ifdef 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
uint32_t uptime_Local = 0 ;
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: Delay Start. var%d <99>=<%s>, max10s? " ) , i + i , rules_vars [ i ] ) ;
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
XdrvRulesProcess ( ) ;
2020-10-28 16:32:07 +00:00
uptime_Local = TasmotaGlobal . uptime ;
while ( uptime_Local + 10 > TasmotaGlobal . uptime & & ( String ) rules_vars [ i ] = = " 99 " ) {
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
loop ( ) ;
}
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: Delay Start. Done " ) ) ;
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
{
2020-11-06 16:09:13 +00:00
//AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: dir %d, delta1 %d, delta2 %d, grant %d"),direction, (Shutter[i].open_max - Shutter[i].real_position) / Shutter[i].close_velocity, Shutter[i].real_position / Shutter[i].close_velocity, 2+Shutter[i].motordelay);
2020-09-09 13:04:57 +01:00
if ( ( ( 1 = = direction ) & & ( ( Shutter [ i ] . open_max - Shutter [ i ] . real_position ) / 100 < = 2 ) )
| | ( ( - 1 = = direction ) & & ( Shutter [ i ] . real_position / Shutter [ i ] . close_velocity < = 2 ) ) ) {
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 ;
switch ( ShutterGlobal . position_mode ) {
2020-09-05 19:39:24 +01:00
# ifdef SHUTTER_STEPPER
case SHT_COUNTER :
2020-09-09 13:04:57 +01:00
analogWriteFreq ( Shutter [ i ] . pwm_velocity ) ;
2020-09-05 19:39:24 +01:00
analogWrite ( Pin ( GPIO_PWM1 , i ) , 0 ) ;
RtcSettings . pulse_counter [ i ] = 0 ;
break ;
# endif
2019-12-04 11:34:27 +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 ) ;
2020-09-09 13:04:57 +01:00
Shutter [ i ] . time = 0 ;
Shutter [ i ] . direction = direction ;
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-02-18 11:42:59 +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 , Shutter[i].direction ,ShutterGlobal.open_velocity_max );
2019-12-02 20:44:05 +00:00
}
2021-02-18 11:42:59 +00:00
//AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Start shtr%d from %d to %d in direction %d"), i, Shutter[i].start_position, Shutter[i].target_position, Shutter[i].direction);
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 :
2020-09-09 13:04:57 +01:00
return Shutter [ i ] . start_position + ( ( Shutter [ i ] . time - Shutter [ i ] . motordelay ) * ( 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 + + ) {
2020-10-28 18:03:39 +00: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.
2020-10-30 11:29:48 +00: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-01-02 14:47:03 +00:00
AddLog_P ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: Shtr%d, Source %s, Powerstate %ld, RelayMask %d, ManualChange %d " ) ,
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
}
2020-09-09 13:04:57 +01:00
switch ( ShutterGlobal . position_mode ) {
2020-09-09 07:58:00 +01:00
// enum Shutterposition_mode {SHT_TIME, SHT_TIME_UP_DOWN, SHT_TIME_GARAGE, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME,};
2020-09-05 19:39:24 +01:00
case SHT_TIME_UP_DOWN :
case SHT_COUNTER :
case SHT_PWM_VALUE :
case SHT_PWM_TIME :
ShutterPowerOff ( i ) ;
switch ( powerstate_local ) {
case 1 :
2020-09-09 13:04:57 +01:00
ShutterStartInit ( i , 1 , Shutter [ i ] . open_max ) ;
2020-09-05 19:39:24 +01:00
break ;
case 3 :
ShutterStartInit ( i , - 1 , 0 ) ;
break ;
default :
2021-02-18 11:42:59 +00:00
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shtr%d Switch OFF motor."),i);
2020-09-09 13:04:57 +01:00
Shutter [ i ] . target_position = Shutter [ i ] . real_position ;
2020-09-05 19:39:24 +01:00
}
break ;
case SHT_TIME :
switch ( powerstate_local ) {
case 1 :
2020-09-09 13:04:57 +01:00
ShutterStartInit ( i , 1 , Shutter [ i ] . open_max ) ;
2020-09-05 19:39:24 +01:00
break ;
case 2 :
ShutterStartInit ( i , - 1 , 0 ) ;
break ;
default :
2021-02-18 11:42:59 +00:00
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shtr%d Switch OFF motor."),i);
2020-09-09 13:04:57 +01:00
Shutter [ i ] . target_position = Shutter [ i ] . real_position ;
2020-09-05 19:39:24 +01:00
}
break ;
case SHT_TIME_GARAGE :
switch ( powerstate_local ) {
case 1 :
2020-09-09 13:04:57 +01:00
ShutterStartInit ( i , Shutter [ i ] . lastdirection * - 1 , Shutter [ i ] . lastdirection = = 1 ? 0 : Shutter [ i ] . open_max ) ;
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: Shtr%d Garage. NewTarget %d " ) , i , Shutter [ i ] . target_position ) ;
2020-09-05 19:39:24 +01:00
break ;
default :
2020-09-09 13:04:57 +01:00
Shutter [ i ] . target_position = Shutter [ i ] . real_position ;
2020-09-05 19:39:24 +01:00
}
2020-09-09 13:04:57 +01:00
} // switch (ShutterGlobal.position_mode)
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: Shtr%d, Target %ld, Powerstatelocal %d " ) , i + 1 , Shutter [ i ] . target_position , powerstate_local ) ;
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
}
2020-01-30 13:33:33 +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 + + ) {
2020-03-02 19:32:55 +00: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
}
2020-01-02 10:23:11 +00:00
void ShutterButtonHandler ( void )
{
uint8_t buttonState = SHT_NOT_PRESSED ;
uint8_t button = XdrvMailbox . payload ;
uint8_t press_index ;
uint32_t button_index = XdrvMailbox . index ;
uint8_t shutter_index = Settings . shutter_button [ button_index ] & 0x03 ;
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 ] ) ) {
if ( Settings . flag . button_single ) { // SetOption13 (0) - Allow only single button press for immediate action
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 ) {
Button . hold_timer [ button_index ] = 0 ;
} else {
Button . hold_timer [ button_index ] + + ;
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
if ( Button . hold_timer [ button_index ] > loops_per_second * Settings . param [ P_HOLD_IGNORE ] / 10 ) {
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
}
}
2020-01-09 13:48:23 +00: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 + + )
2020-01-30 13:33:33 +00: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 ;
}
2020-01-22 12:23:59 +00: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
}
}
if ( ! Settings . flag . button_single ) { // SetOption13 (0) - Allow multi-press
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-01-02 14:47:03 +00:00
Settings . shutter_button [ i ] , shutter_index , Button . press_counter [ i ] , min_shutterbutton_press_counter , i ) ;
2020-03-02 19:32:55 +00:00
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 + + )
2020-02-24 11:23:03 +00: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 ) {
if ( ( ! Settings . flag . button_restrict ) & & ( ( ( press_index > = 5 ) & & ( press_index < = 7 ) ) | | ( buttonState = = SHT_PRESSED_EXT_HOLD ) | | ( buttonState = = SHT_PRESSED_EXT_HOLD_SIMULTANEOUS ) ) ) {
// 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 + + ) {
2020-03-02 19:32:55 +00:00
if ( ( Settings . shutter_button [ i ] & ( 1 < < 31 ) ) & & ( ( Settings . shutter_button [ i ] & 0x03 ) = = shutter_index ) ) {
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 ) ;
return ;
} 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 ) ;
return ;
}
}
if ( buttonState < = SHT_PRESSED_IMMEDIATE ) {
2020-01-22 12:23:59 +00:00
if ( Settings . shutter_startrelay [ shutter_index ] & & Settings . shutter_startrelay [ shutter_index ] < 9 ) {
uint8_t pos_press_index = ( buttonState = = SHT_PRESSED_HOLD ) ? 3 : ( press_index - 1 ) ;
if ( pos_press_index > 3 ) pos_press_index = 3 ;
2021-01-02 14:47:03 +00:00
AddLog_P ( 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 {
2020-01-22 12:23:59 +00:00
uint8_t position = ( Settings . shutter_button [ button_index ] > > ( 6 * pos_press_index + 2 ) ) & 0x03f ;
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 {
CmndShutterPosition ( ) ;
}
2020-01-22 12:23:59 +00:00
if ( Settings . shutter_button [ button_index ] & ( ( 0x01 < < 26 ) < < pos_press_index ) ) {
// MQTT broadcast to grouptopic
char scommand [ CMDSZ ] ;
char stopic [ TOPSZ ] ;
for ( uint32_t i = 0 ; i < MAX_SHUTTERS ; i + + ) {
if ( ( i = = shutter_index ) | | ( Settings . shutter_button [ button_index ] & ( 0x01 < < 30 ) ) ) {
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)
} // if (Settings.shutter)
} // ende else
} // if (position)
} // end else
} // 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
}
}
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-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " SHT: Payload toggle: %d, i %d, dir %d " ) , XdrvMailbox . payload , XdrvMailbox . index , dir ) ;
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 ( ) ;
}
}
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);
2020-01-04 14:09:57 +00:00
if ( ( 1 = = XdrvMailbox . index ) & & ( 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);
2020-01-04 14:09:57 +00:00
if ( ( 1 = = XdrvMailbox . index ) & & ( 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 ;
2019-12-02 20:44:05 +00:00
XdrvMailbox . data_len = 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 ) ) {
2020-01-12 13:18:15 +00:00
if ( ! ( Settings . shutter_options [ XdrvMailbox . index - 1 ] & 2 ) ) {
if ( ( 1 = = XdrvMailbox . index ) & & ( XdrvMailbox . payload ! = - 99 ) ) {
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
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " SHT: Stop moving %d: dir: %d " ) , XdrvMailbox . index , Shutter [ i ] . direction ) ;
2020-12-18 08:19:45 +00:00
Shutter [ i ] . target_position = Shutter [ i ] . real_position ;
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 )
{
2020-11-06 16:09:13 +00:00
//AddLog_P(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 ) ) {
2020-01-12 13:18:15 +00:00
if ( ! ( Settings . shutter_options [ XdrvMailbox . index - 1 ] & 2 ) ) {
uint32_t index = XdrvMailbox . index - 1 ;
//limit the payload
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: Pos. in: payload %s (%d), payload %d, idx %d, src %d " ) , XdrvMailbox . data , XdrvMailbox . data_len , XdrvMailbox . payload , XdrvMailbox . index , TasmotaGlobal . last_source ) ;
2020-01-12 13:18:15 +00:00
// value 0 with data_len > 0 can mean Open
2020-02-24 11:23:03 +00:00
// special handling fo UP,DOWN,TOGGLE,STOP command comming with payload -99
2020-01-12 13:18:15 +00:00
if ( ( XdrvMailbox . data_len > 1 ) & & ( XdrvMailbox . payload < = 0 ) ) {
//UpperCase(XdrvMailbox.data, XdrvMailbox.data);
2020-09-09 13:04:57 +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 ;
}
2020-09-09 13:04:57 +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
XdrvMailbox . payload = - 99 ;
CmndShutterStop ( ) ;
return ;
}
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 ) ;
2020-01-12 13:18:15 +00:00
// webgui still send also on inverted shutter the native position.
2020-10-30 11:29:48 +00:00
target_pos_percent = ( ( Settings . shutter_options [ index ] & 1 ) & & ( SRC_WEBGUI ! = TasmotaGlobal . last_source ) ) ? 100 - target_pos_percent : target_pos_percent ;
2020-01-12 13:18:15 +00:00
if ( XdrvMailbox . payload ! = - 99 ) {
//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);
//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-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: lastsource %d:, real %d, target %d, payload %d " ) , TasmotaGlobal . last_source , Shutter [ index ] . real_position , Shutter [ index ] . target_position , target_pos_percent ) ;
2020-01-12 13:18:15 +00:00
}
2020-09-09 13:04:57 +01:00
if ( ( target_pos_percent > = 0 ) & & ( target_pos_percent < = 100 ) & & abs ( Shutter [ index ] . target_position - Shutter [ index ] . real_position ) / Shutter [ index ] . close_velocity > 2 ) {
2020-01-27 08:46:39 +00:00
if ( Settings . shutter_options [ index ] & 4 ) {
2020-09-09 13:04:57 +01:00
if ( 0 = = target_pos_percent ) Shutter [ index ] . target_position - = 1 * RESOLUTION * STEPS_PER_SECOND ;
if ( 100 = = target_pos_percent ) Shutter [ index ] . target_position + = 1 * RESOLUTION * STEPS_PER_SECOND ;
2020-01-27 08:46:39 +00:00
}
2020-09-09 13:04:57 +01:00
int8_t new_shutterdirection = Shutter [ index ] . real_position < Shutter [ index ] . target_position ? 1 : - 1 ;
if ( Shutter [ index ] . direction = = - new_shutterdirection ) {
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 ) ;
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
ExecuteCommandPowerShutter ( Settings . shutter_startrelay [ index ] + 1 , new_shutterdirection = = 1 ? 0 : 1 , SRC_SHUTTER ) ;
// power on
ExecuteCommandPowerShutter ( Settings . shutter_startrelay [ index ] , 1 , SRC_SHUTTER ) ;
2020-06-22 12:21:13 +01:00
}
2021-02-18 11:59:11 +00: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 ) {
2020-10-28 18:03:39 +00:00
if ( ( TasmotaGlobal . power > > ( Settings . shutter_startrelay [ index ] - 1 ) ) & 3 > 0 ) {
2020-09-09 13:04:57 +01:00
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
}
ExecuteCommandPowerShutter ( Settings . shutter_startrelay [ index ] + ( new_shutterdirection = = 1 ? 0 : 1 ) , 1 , SRC_SHUTTER ) ;
}
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 + + ) {
2020-09-05 19:39:24 +01:00
ExecuteCommandPowerShutter ( Settings . shutter_startrelay [ index ] , 1 , SRC_SHUTTER ) ;
delay ( 500 ) ;
ExecuteCommandPowerShutter ( Settings . shutter_startrelay [ index ] , 0 , SRC_SHUTTER ) ;
delay ( 500 ) ;
}
// 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])
2020-09-05 19:39:24 +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)
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 ) ;
2020-04-21 15:19:42 +01:00
ShutterReportPosition ( true , index ) ;
2019-09-29 17:00:01 +01:00
}
2020-01-12 13:18:15 +00:00
XdrvMailbox . index = index + 1 ; // Fix random index for ShutterClose
if ( XdrvMailbox . command )
ResponseCmndIdxNumber ( ( Settings . shutter_options [ index ] & 1 ) ? 100 - target_pos_percent : target_pos_percent ) ;
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 ( ) ;
}
}
}
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 ) {
2019-10-01 16:19:37 +01:00
Settings . shutter_opentime [ XdrvMailbox . index - 1 ] = ( uint16_t ) ( 10 * CharToFloat ( XdrvMailbox . data ) ) ;
2019-09-29 17:00:01 +01:00
ShutterInit ( ) ;
}
char time_chr [ 10 ] ;
2019-10-01 16:19:37 +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 ) ;
}
}
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 ) {
2019-10-01 16:19:37 +01:00
Settings . shutter_closetime [ XdrvMailbox . index - 1 ] = ( uint16_t ) ( 10 * CharToFloat ( XdrvMailbox . data ) ) ;
2019-09-29 17:00:01 +01:00
ShutterInit ( ) ;
}
char time_chr [ 10 ] ;
2019-10-01 16:19:37 +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 ) ;
}
}
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 ) {
2020-09-08 18:34:10 +01:00
Settings . shutter_motordelay [ XdrvMailbox . index - 1 ] = ( uint16_t ) ( STEPS_PER_SECOND * CharToFloat ( XdrvMailbox . data ) ) ;
2019-10-30 08:28:46 +00:00
ShutterInit ( ) ;
}
char time_chr [ 10 ] ;
2020-09-08 18:34:10 +01:00
dtostrfd ( ( float ) ( Settings . shutter_motordelay [ XdrvMailbox . index - 1 ] ) / STEPS_PER_SECOND , 2 , time_chr ) ;
2019-10-30 08:28:46 +00:00
ResponseCmndIdxChar ( time_chr ) ;
}
}
2020-09-05 19:39:24 +01:00
void CmndShutterMode ( void )
{
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < = MAX_MODES ) ) {
2020-09-09 13:04:57 +01:00
ShutterGlobal . position_mode = XdrvMailbox . payload ;
2020-09-05 19:39:24 +01:00
Settings . shutter_mode = XdrvMailbox . payload ;
ShutterInit ( ) ;
}
2020-09-09 13:04:57 +01:00
ResponseCmndNumber ( ShutterGlobal . position_mode ) ;
2020-09-05 19:39:24 +01:00
}
2019-09-29 17:00:01 +01:00
void CmndShutterRelay ( void )
{
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = MAX_SHUTTERS ) ) {
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < = 64 ) ) {
2019-10-01 16:19:37 +01:00
Settings . shutter_startrelay [ XdrvMailbox . index - 1 ] = XdrvMailbox . payload ;
2019-09-29 17:00:01 +01:00
if ( XdrvMailbox . payload > 0 ) {
2020-09-09 13:04:57 +01:00
ShutterGlobal . RelayShutterMask | = 3 < < ( XdrvMailbox . payload - 1 ) ;
2019-09-29 17:00:01 +01:00
} else {
2020-09-09 13:04:57 +01:00
ShutterGlobal . RelayShutterMask ^ = 3 < < ( Settings . shutter_startrelay [ XdrvMailbox . index - 1 ] - 1 ) ;
2019-09-29 17:00:01 +01:00
}
2019-10-01 16:19:37 +01:00
Settings . shutter_startrelay [ XdrvMailbox . index - 1 ] = XdrvMailbox . payload ;
2019-09-29 17:00:01 +01:00
ShutterInit ( ) ;
// if payload is 0 to disable the relay there must be a reboot. Otherwhise does not work
}
ResponseCmndIdxNumber ( Settings . shutter_startrelay [ XdrvMailbox . index - 1 ] ) ;
}
}
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 + + )
2020-01-04 15:24:45 +00:00
if ( ( Settings . shutter_button [ i ] & 0x3 ) = = ( XdrvMailbox . index - 1 ) )
Settings . shutter_button [ i ] = 0 ;
} else {
if ( setting ) {
// anything was set
setting | = ( 1 < < 31 ) ;
setting | = ( XdrvMailbox . index - 1 ) & 0x3 ;
}
Settings . shutter_button [ button_index - 1 ] = setting ;
}
}
}
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 + + ) {
2020-01-04 15:24:45 +00:00
setting = Settings . shutter_button [ i ] ;
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 ) ) {
2020-01-12 13:18:15 +00:00
Settings . shutter_set50percent [ XdrvMailbox . index - 1 ] = ( Settings . shutter_options [ XdrvMailbox . index - 1 ] & 1 ) ? 100 - XdrvMailbox . payload : XdrvMailbox . payload ;
2020-10-29 11:44:05 +00:00
Settings . shuttercoeff [ 0 ] [ XdrvMailbox . index - 1 ] = 0 ;
2019-09-29 17:00:01 +01:00
ShutterInit ( ) ;
}
2020-01-12 13:18:15 +00: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 ) {
2020-09-09 13:04:57 +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 ;
2019-10-01 16:19:37 +01:00
ShutterStartInit ( XdrvMailbox . index - 1 , 0 , 0 ) ;
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 ;
ShutterStartInit ( XdrvMailbox . index - 1 , 0 , Shutter [ XdrvMailbox . index - 1 ] . open_max ) ;
2020-04-21 15:20:20 +01:00
Settings . shutter_position [ XdrvMailbox . index - 1 ] = 100 ;
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.
2020-09-09 15:23:50 +01:00
for ( char * str = strtok_r ( data_copy , " " , & str_ptr ) ; str & & i < 2 ; str = strtok_r ( nullptr , " " , & str_ptr ) , i + + ) {
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 ;
}
Settings . shutter_pwmrange [ i ] [ XdrvMailbox . index - 1 ] = field ;
}
2021-01-23 15:26:23 +00: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 " ;
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 ] ) ;
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.
for ( char * str = strtok_r ( data_copy , " " , & str_ptr ) ; str & & i < 5 ; str = strtok_r ( nullptr , " " , & str_ptr ) , i + + ) {
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 ;
}
2020-10-29 11:44:05 +00:00
Settings . shutter_set50percent [ XdrvMailbox . index - 1 ] = 50 ;
2020-01-09 10:35:01 +00:00
for ( i = 0 ; i < 5 ; i + + ) {
2020-01-13 08:48:29 +00:00
Settings . shuttercoeff [ i ] [ XdrvMailbox . index - 1 ] = SHT_DIV_ROUND ( ( uint32_t ) messwerte [ i ] * 1000 , messwerte [ 4 ] ) ;
2021-01-23 15:26:23 +00:00
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 " ;
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 ] ) ;
ResponseCmndIdxChar ( setting_chr ) ;
2019-09-29 17:00:01 +01:00
}
}
}
2020-09-09 13:04:57 +01: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 ) {
2020-09-09 13:04:57 +01:00
Settings . shutter_options [ XdrvMailbox . index - 1 ] & = ~ ( option ) ;
2020-01-12 13:18:15 +00:00
} else if ( XdrvMailbox . payload = = 1 ) {
2020-09-09 13:04:57 +01:00
Settings . shutter_options [ XdrvMailbox . index - 1 ] | = ( option ) ;
2020-01-12 13:18:15 +00:00
}
2020-09-09 13:04:57 +01:00
ResponseCmndIdxNumber ( ( Settings . shutter_options [ XdrvMailbox . index - 1 ] & option ) ? 1 : 0 ) ;
2020-01-12 13:18:15 +00:00
}
}
2020-09-09 13:04:57 +01:00
void CmndShutterInvert ( void ) {
ShutterOptionsSetHelper ( 1 ) ;
}
void CmndShutterLock ( void ) {
ShutterOptionsSetHelper ( 2 ) ;
}
2020-01-13 11:00:34 +00:00
void CmndShutterEnableEndStopTime ( void ) {
2020-09-09 13:04:57 +01:00
ShutterOptionsSetHelper ( 4 ) ;
2020-01-13 11:00:34 +00:00
}
2020-09-09 13:04:57 +01:00
void CmndShutterInvertWebButtons ( void ) {
ShutterOptionsSetHelper ( 8 ) ;
2020-03-10 07:41:37 +00:00
}
2019-09-29 17:00:01 +01:00
/*********************************************************************************************\
* Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool Xdrv27 ( uint8_t function )
{
bool result = false ;
2019-11-03 12:51:22 +00:00
if ( Settings . flag3 . shutter_mode ) { // SetOption80 - Enable shutter support
2019-09-29 17:00:01 +01:00
switch ( function ) {
case FUNC_PRE_INIT :
ShutterInit ( ) ;
break ;
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 ;
2019-12-14 11:09:35 +00:00
2019-09-29 17:00:01 +01:00
case FUNC_COMMAND :
result = DecodeCommand ( kShutterCommands , ShutterCommand ) ;
break ;
case FUNC_JSON_APPEND :
2020-10-30 11:29:48 +00:00
for ( uint8_t i = 0 ; i < TasmotaGlobal . shutters_present ; i + + ) {
2020-02-27 07:32:05 +00:00
uint8_t position = ( Settings . shutter_options [ i ] & 1 ) ? 100 - Settings . shutter_position [ i ] : Settings . shutter_position [ i ] ;
2020-09-09 13:04:57 +01:00
uint8_t target = ( Settings . shutter_options [ i ] & 1 ) ? 100 - ShutterRealToPercentPosition ( Shutter [ i ] . target_position , i ) : ShutterRealToPercentPosition ( Shutter [ i ] . target_position , i ) ;
2020-02-27 07:32:05 +00:00
2019-09-29 17:00:01 +01:00
ResponseAppend_P ( " , " ) ;
2020-09-09 13:04:57 +01:00
ResponseAppend_P ( JSON_SHUTTER_POS , i + 1 , position , Shutter [ i ] . direction , target ) ;
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 :
char stemp1 [ 10 ] ;
// 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 ;
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: Switched relay %d by %s " ) , ShutterGlobal . RelayCurrentMask , GetTextIndexed ( stemp1 , sizeof ( stemp1 ) , TasmotaGlobal . last_source , kCommandSource ) ) ;
2019-09-30 10:21:43 +01:00
ShutterRelayChanged ( ) ;
2020-09-09 13:04:57 +01:00
ShutterGlobal . RelayOldMask = XdrvMailbox . index ;
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 ) {
2019-12-12 15:14:06 +00:00
uint8_t i ;
2020-10-30 11:29:48 +00:00
for ( i = 0 ; i < TasmotaGlobal . devices_present ; i + + ) {
2020-09-09 13:04:57 +01:00
if ( ShutterGlobal . RelayCurrentMask & 1 ) {
2019-12-12 15:14:06 +00:00
break ;
}
2020-09-09 13:04:57 +01:00
ShutterGlobal . RelayCurrentMask > > = 1 ;
2019-12-12 15:14:06 +00:00
}
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 ;
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " SHT: Skipping switch off relay %d " ) , i ) ;
2020-09-05 19:39:24 +01:00
ExecuteCommandPowerShutter ( i + 1 , 0 , SRC_SHUTTER ) ;
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 :
if ( Settings . shutter_button [ XdrvMailbox . index ] & ( 1 < < 31 ) ) {
ShutterButtonHandler ( ) ;
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
Settings . shuttercoeff [ 0 ] [ 0 ] = 0 ;
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 " ) ) ;
}
Settings . shuttercoeff [ 0 ] [ 0 ] = 0 ;
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