#include "app/led/led.h"
#include "core/csect.h"
#include "core/main.h"
#include "core/config.h"
#include "core/config_pins.h"
#include "app/rseq/rseq.h"

//#define LED_LOWBAT_FLARE_INTERVAL       5000
//#define LED_LOWBAT_FLARE_TIME           10
//#define LED_POLARITY_INVERTED           0

#define LED_LOWBAT_FLARE_INTERVAL       CONFIG_LED_LOWBAT_FLASH_INTERVAL
#define LED_LOWBAT_FLARE_TIME           CONFIG_LED_LOWBAT_FLASH_TIME
#define LED_POLARITY_INVERTED           CONFIG_LED_POLARITY_INVERTED
#define LED_OKBAT_FLARE_ENABLE          CONFIG_LED_OKBAT_FLASH_ENABLE
#define LED_OKBAT_FLARE_INTERVAL        CONFIG_LED_OKBAT_FLASH_INTERVAL
#define LED_OKBAT_FLARE_TIME            CONFIG_LED_OKBAT_FLASH_TIME
#define LED_SIGNALING_PERIOD            CONFIG_LED_SIGNALING_PERIOD
#define LED_SIGNALING_TIME              CONFIG_LED_SIGNALING_TIME 
#define LED_AUTOMATALARM_COMBINED       0
static volatile eLedMode_t g_LedMode = eLedMode_Idle; 

static volatile bool g_bHarmUpEnable = false;
static volatile uint32_t g_nHarmUpTimestamp = 0ul;

// -----------
#pragma pack(push, 1)
typedef struct
{
   struct
   {    
      uint32_t gp_timer_counter;
      uint32_t gp_timer_maximum;
      uint32_t gp_counter_signaling;
   }
   counters;
   
   struct
   {
      bool bLowBatterySignal;      
      bool bUSBActiveSignal;
      bool bSignalStage;   // false during flashing, true during waiting next flash      
      bool bAutomatAlarm;
      bool bHarmupComplete; // false during harmup interval since startup, the it becomes true
   }
   state;
}
sData_t;
#pragma pack(pop)
// -----------
static bool seqirq_state_startup( void * arg );
static bool seqirq_state_signaling( void * arg );
static bool seqirq_state_signaling_rollover( void * arg );
static bool seqirq_state_timer_nextstate( void * arg );
#if LED_OKBAT_FLARE_ENABLE == 0
static bool seqirq_state_check_lowbat( void * arg );
#endif
#if 0 // useless
static bool seqirq_state_timer_prevstate( void * arg );
static bool seqirq_state_lowbat_serve( void * arg );
static bool seqirq_state_check_usbactive( void * arg );
static bool seqirq_state_update_usbactive( void * arg );
#endif
static bool seqirq_state_update_ledstatus( void * arg );
// -----------
static fRoutine_t *       rseq_timer_list[ 5 ];
static sRoutineSequence_t rseq_timer;
static sData_t            g_sData;


static bool IFace_Led_Init();
static void IFace_Led_Tick();
static bool IFace_Led_SetMode( eLedMode_t mode );
static void IFace_Led_SetLowBattery( bool batteryLow );
static void IFace_Led_SetUSBConnectivity( bool usbActive );
static void IFace_Led_SetAutomatAlarm( bool automatAlarm );  
static void IFace_Led_SetHarmupStatus( bool harmupCompleted );
static bool IFace_Led_DeInit();
static void harmup_init();
static void harmup_serve();
static void harmup_stop();

const sLED_Handle_t LEDHandle = {

     .Init = IFace_Led_Init,
     .Tick = IFace_Led_Tick,
     .SetMode = IFace_Led_SetMode,
     .SetLowBattery = IFace_Led_SetLowBattery,
     .SetUSBConnectivity = IFace_Led_SetUSBConnectivity,
     .SetAutomatAlarm = IFace_Led_SetAutomatAlarm,
     .SetHarmupStatus = IFace_Led_SetHarmupStatus,
     .DeInit = IFace_Led_DeInit,
     .harmup_init = harmup_init,
     .harmup_serve = harmup_serve,
     .harmup_stop  = harmup_stop
};

static inline bool seqTake( void * arg )
{
   DI();
   return true;
}

static inline bool seqFree( void * arg )
{
   EI();
   return true;
}

//#if CONFIG_HARMUP_INTERVAL > 0
static void harmup_init()
{
  g_bHarmUpEnable = true;
  g_nHarmUpTimestamp = HAL_GetTick();

  LEDHandle.SetHarmupStatus( !g_bHarmUpEnable ); 
}

static void harmup_serve()
{
  if( g_bHarmUpEnable )
  {
      if( (HAL_GetTick() - g_nHarmUpTimestamp)/1000 > (CONFIG_HARMUP_INTERVAL) )
      {
          g_bHarmUpEnable = false;

          LEDHandle.SetHarmupStatus( !g_bHarmUpEnable ); 
      }
  }
}

static void harmup_stop()
{
     g_bHarmUpEnable = false;
     LEDHandle.SetHarmupStatus( !g_bHarmUpEnable ); 
}
//#endif

void ledGreen( bool state )
{
   #if LED_POLARITY_INVERTED
   HAL_GPIO_WritePin( CONFIG_PORT__LED_GREEN, CONFIG_PIN__LED_GREEN, (state)?GPIO_PIN_RESET:GPIO_PIN_SET );
   #else
   HAL_GPIO_WritePin( CONFIG_PORT__LED_GREEN, CONFIG_PIN__LED_GREEN, (state)?GPIO_PIN_SET:GPIO_PIN_RESET );
   #endif
}

void ledRed( bool state )
{
   #if LED_POLARITY_INVERTED
   HAL_GPIO_WritePin( CONFIG_PORT__LED_RED, CONFIG_PIN__LED_RED, (state)?GPIO_PIN_RESET:GPIO_PIN_SET );
   #else
   HAL_GPIO_WritePin( CONFIG_PORT__LED_RED, CONFIG_PIN__LED_RED, (state)?GPIO_PIN_SET:GPIO_PIN_RESET );
   #endif
}

void setLedColor( eLed_color_t color )
{
    switch ( color )
    {
        case eLed_color_off:
        {
            ledRed(false);
            ledGreen(false);
        }
        break;
        case eLed_color_red:
        {
            ledRed(true);
            ledGreen(false);
        }
        break;
        case eLed_color_grn:
        {
            ledRed(false);
            ledGreen(true);
        }
        break;
        case eLed_color_orange:
        {
            ledRed(true);
            ledGreen(true);
        }
        break;
    }
}

static bool IFace_Led_Init()
{
    ledGreen( false );
    ledRed( false );

    #if LED_POLARITY_INVERTED
    {
        GPIO_InitTypeDef GPIO_InitStruct = {0};

        // Configure pin: OpenDrain with no pulls
        GPIO_InitStruct.Pin = CONFIG_PIN__LED_RED;  
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;        

        HAL_GPIO_Init(CONFIG_PORT__LED_RED, &GPIO_InitStruct);
    }
    {
        GPIO_InitTypeDef GPIO_InitStruct = {0};

        // Configure pin: OpenDrain with no pulls
        GPIO_InitStruct.Pin = CONFIG_PIN__LED_GREEN;  
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;        

        HAL_GPIO_Init(CONFIG_PORT__LED_GREEN, &GPIO_InitStruct);
    }
    #else
    {
        GPIO_InitTypeDef GPIO_InitStruct = {0};

        // Configure pin: OpenDrain with no pulls
        GPIO_InitStruct.Pin = CONFIG_PIN__LED_RED;  
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;        

        HAL_GPIO_Init(CONFIG_PORT__LED_RED, &GPIO_InitStruct);
    }
    {
        GPIO_InitTypeDef GPIO_InitStruct = {0};

        // Configure pin: OpenDrain with no pulls
        GPIO_InitStruct.Pin = CONFIG_PIN__LED_GREEN;  
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;        

        HAL_GPIO_Init(CONFIG_PORT__LED_GREEN, &GPIO_InitStruct);
    }
    #endif
    ledRed(false);
    ledGreen(false);
    
    seqTake(&g_sData);
    g_sData.counters.gp_counter_signaling = 0;
    g_sData.counters.gp_timer_counter = 0;
    g_sData.counters.gp_timer_maximum = 0;
    g_sData.state.bLowBatterySignal = false;
    g_sData.state.bSignalStage = false;
    g_sData.state.bUSBActiveSignal = false;    
    g_sData.state.bAutomatAlarm = false;
    g_sData.state.bHarmupComplete = false;

    seqFree(&g_sData);

    rsa_sequence_init( &rseq_timer,  &g_sData, rseq_timer_list, sizeof(rseq_timer_list)/sizeof(*rseq_timer_list) );   
    rsa_sequence_setlockers( &rseq_timer, seqTake, seqFree );

    IFace_Led_SetMode( eLedMode_Signaling );
     rsa_sequence_insert_routine( &rseq_timer, &seqirq_state_startup );   
    return true;
}

eLed_color_t GET_LED_COLOR_STATE( int number )
{
    eLed_color_t led_color =  eLed_color_off;
//    switch (number)
//    {
//        case LED1:
//        {
//            led_color |= HAL_GPIO_ReadPin( CONFIG_PORT_28LED_RED,     CONFIG_PIN_28LED_RED ) == GPIO_PIN_RESET ? eLed_color_red : eLed_color_off;
//            led_color |= HAL_GPIO_ReadPin( CONFIG_PORT_28LED_GREEN,   CONFIG_PIN_28LED_GREEN) == GPIO_PIN_RESET ? eLed_color_grn : eLed_color_off;
//        }break;
//        case LED2:
//        {
//            led_color |= HAL_GPIO_ReadPin( CONFIG_PORT__LED_GREEN, CONFIG_PIN__LED_GREEN)== GPIO_PIN_RESET ? eLed_color_grn : eLed_color_off;
//            led_color |= HAL_GPIO_ReadPin( CONFIG_PORT__LED_RED,   CONFIG_PIN__LED_RED ) == GPIO_PIN_RESET ? eLed_color_red : eLed_color_off;
//        }break;
//    }
    return led_color;
}

static void IFace_Led_Tick()
{
   if( rsa_sequence_icall( &rseq_timer ) )
   {
       rsa_sequence_ireset( &rseq_timer );       
   }   
}

static bool IFace_Led_SetMode( eLedMode_t mode )
{
   bool bSet = true;

   switch( mode )
   {
      case eLedMode_Idle:
      {
           rsa_sequence_clear( &rseq_timer );

           ledGreen( false );
           ledRed( false );
      }
      break;

      case eLedMode_Signaling:
      {
           seqTake(&g_sData);
             rsa_sequence_iclear( &rseq_timer );
             rsa_sequence_iinsert_routine( &rseq_timer, &seqirq_state_signaling ); 
             rsa_sequence_iinsert_routine( &rseq_timer, &seqirq_state_timer_nextstate );             
             rsa_sequence_iinsert_routine( &rseq_timer, &seqirq_state_signaling_rollover ); 

             g_sData.counters.gp_counter_signaling = 1 + (LED_SIGNALING_TIME / LED_SIGNALING_PERIOD);
             g_sData.state.bSignalStage = false;

           seqFree(&g_sData);

           ledGreen( false );
           ledRed( false );
      }
      break;

      case eLedMode_Normal:
      {
           rsa_sequence_clear( &rseq_timer );
           rsa_sequence_insert_routine( &rseq_timer, &seqirq_state_startup );              
      }
      break;

      default: bSet = false;
   }

   if( bSet )
   {
      __DI__ g_LedMode = mode; __EI__
   }

   return bSet;
}

static void IFace_Led_SetLowBattery( bool batteryLow )
{
    seqTake(&g_sData);
    if( g_sData.state.bLowBatterySignal ^ batteryLow )
    {
        rsa_sequence_ireset( &rseq_timer );  // make @seqirq_state_update_usbactive to be called
        g_sData.state.bLowBatterySignal = batteryLow;
        g_sData.state.bSignalStage = false;
    }
    seqFree(&g_sData);
}

static void IFace_Led_SetAutomatAlarm( bool automatAlarm )
{
    seqTake(&g_sData);
    if( g_sData.state.bAutomatAlarm ^ automatAlarm )
    {
        rsa_sequence_ireset( &rseq_timer );  // make @seqirq_state_update_usbactive to be called
        g_sData.state.bAutomatAlarm = automatAlarm;
        g_sData.state.bSignalStage = false;
    }
    seqFree(&g_sData);
}

static void IFace_Led_SetUSBConnectivity( bool usbActive )
{
    seqTake(&g_sData);
    if( g_sData.state.bUSBActiveSignal ^ usbActive )
    {
        rsa_sequence_ireset( &rseq_timer );  // make @seqirq_state_update_usbactive to be called
        g_sData.state.bUSBActiveSignal = usbActive;
    }
    seqFree(&g_sData);
}

static void IFace_Led_SetHarmupStatus( bool harmupCompleted )
{
    seqTake(&g_sData);
    if( g_sData.state.bHarmupComplete ^ harmupCompleted )
    {
        rsa_sequence_ireset( &rseq_timer );  // make @seqirq_state_update_usbactive to be called
        g_sData.state.bHarmupComplete = harmupCompleted;
    }
    seqFree(&g_sData);
}
static bool IFace_Led_DeInit()
{
    seqTake(&g_sData);
    rsa_sequence_iclear( &rseq_timer );
    seqFree(&g_sData);

    ledGreen( false );
    ledRed( false );
    {
        GPIO_InitTypeDef GPIO_InitStruct = {0};

        // Configure the pin muxing
        GPIO_InitStruct.Pin = CONFIG_PIN__LED_RED;  
        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;        

        HAL_GPIO_Init(CONFIG_PORT__LED_RED, &GPIO_InitStruct);
    }
    {
        GPIO_InitTypeDef GPIO_InitStruct = {0};

        // Configure the pin muxing
        GPIO_InitStruct.Pin = CONFIG_PIN__LED_GREEN;  
        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;        

        HAL_GPIO_Init(CONFIG_PORT__LED_GREEN, &GPIO_InitStruct);
    }

    return true;
}

//------------------------------------------------------------------------------
// {IFace_Led_SetMode} => [seqirq_state_startup]
/* IRQ */static bool seqirq_state_startup( void * arg )
{
   sData_t * pData = (sData_t*)(arg);
   
   ledGreen( false );
   ledRed( false );
   
   rsa_sequence_iclear( &rseq_timer );

   // Note: seqirq_state_update_ledstatus is responsible for LED indicator control.
   // LED Indicator algorythm: see @seqirq_state_update_ledstatus routine for details

   rsa_sequence_iinsert_routine( &rseq_timer, &seqirq_state_update_ledstatus ); // never ends
   (void)pData;
   return false;  // return false to prevent calling next routine
} 
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// {IFace_Led_SetMode} => [seqirq_state_signaling]
/* IRQ */static bool seqirq_state_signaling( void * arg )
{
   sData_t * pData = (sData_t*)(arg);
      
   ledGreen( pData->state.bSignalStage );
   ledRed( !pData->state.bSignalStage );
   
   pData->counters.gp_timer_counter = 0;
   pData->counters.gp_timer_maximum = LED_SIGNALING_PERIOD;

   return true;  // go to the timer routine
} 
//------------------------------------------------------------------------------


//------------------------------------------------------------------------------
// {IFace_Led_SetMode} => [seqirq_state_signaling_rollover]
/* IRQ */static bool seqirq_state_signaling_rollover( void * arg )
{
   sData_t * pData = (sData_t*)(arg);

   if( pData->counters.gp_counter_signaling > 0 )
   {
       pData->counters.gp_counter_signaling --;
   }

   if( pData->counters.gp_counter_signaling > 0 )
   {
       rsa_sequence_ireset( &rseq_timer );

       pData->state.bSignalStage = !pData->state.bSignalStage;
   }
   else
   {
       IFace_Led_SetMode( eLedMode_Normal );
   }

   return false;  // go to the timer routine
} 
//------------------------------------------------------------------------------
#if 0 // useless
// {seqirq_state_startup => seqirq_state_update_usbactive}
/* IRQ */ static bool seqirq_state_update_usbactive( void * arg )
{
   sData_t * pData = (sData_t*)(arg);

   #if LED_AUTOMATALARM_COMBINED == 0
   // Single mode: check for USB-active first
   if( pData->state.bUSBActiveSignal )
   {
       // If USB-active is asserted:
       ledRed( false );               
       ledGreen( true );              // Show GREEN indicator
   }
   else
       // Then check for Alarm
       if( pData->state.bAutomatAlarm )  
       {
           // If Automat Alarm is asserted:
           ledRed( true );               // Show RED indicator
           ledGreen( false );            
       }
       else
       {
           ledRed( false );              // Blow both indicators
           ledGreen( false );            
       }
   #else
   // Combined mode: check for USB-active flag only.
   // Check if USB-Active signal is asserted and update USB-activity LED
   ledGreen( pData->state.bUSBActiveSignal );
   #endif
      
   return true;  // let the next routine to be called
}
#endif
//------------------------------------------------------------------------------
// {seqirq_state_startup => seqirq_state_update_usbactive}
/* IRQ */ static bool seqirq_state_update_ledstatus( void * arg )
{
   sData_t * pData = (sData_t*)(arg);

   if( //pData->state.bLowBatterySignal         // LOW BATTERY
       //||
       //pData->state.bAutomatAlarm             // AUTOMAT SIGNAL ALARM
       //||
       (!pData->state.bHarmupComplete)        // DEVICE IS STILL HARMING
   )
   {
       ledRed( true );                        // SHOW RED INDICATOR
       ledGreen( false );
   }
   else
   {
       ledRed( false );
       ledGreen( true );                      // SHOW GREEN INDICATOR
   }
 
   return false;  // false: never leave this routine
}

//------------------------------------------------------------------------------
#if 0 // useless
// {seqirq_state_startup => seqirq_state_update_usbactive => seqirq_state_check_usbactive}
/* IRQ */ static bool seqirq_state_check_usbactive( void * arg )
{
   sData_t * pData = (sData_t*)(arg);

   #if LED_AUTOMATALARM_COMBINED == 0
   // Single mode: show only one indicator - either USB activity, or Alarm, or battery mode
   // Check if USB-Active or Automat Alarm signals are asserted
   if( pData->state.bUSBActiveSignal || pData->state.bAutomatAlarm )
   {
       // If asserted, return false to prevent calling next routine
       return false;
   }
   #else
   // Combined mode: wait for USB disconnection to enter combined indication mode
   // Check if USB-Active signal is asserted
   if( pData->state.bUSBActiveSignal )
   {
       // If asserted, return false to prevent calling next routine
       return false;
   }
   #endif

   return true;  // let the next routine to be called
}
#endif
//------------------------------------------------------------------------------
#if LED_OKBAT_FLARE_ENABLE == 0
// {seqirq_state_startup => seqirq_state_update_usbactive => seqirq_state_check_usbactive => seqirq_state_check_lowbat}
/* IRQ */ static bool seqirq_state_check_lowbat( void * arg )
{
   sData_t * pData = (sData_t*)(arg);
   // If LowBattery signal is not asserted, stay in this routine
   // If LowBattery signal is asserted, @bLowBatterySignal is true, so
   // returning 'true' causes the next routine to be called.
   return pData->state.bLowBatterySignal;
}
#endif
//------------------------------------------------------------------------------
#if 0 // useless
// {seqirq_state_startup => seqirq_state_update_usbactive => seqirq_state_check_usbactive => seqirq_state_check_lowbat => seqirq_state_lowbat_serve}
/* IRQ */ static bool seqirq_state_lowbat_serve( void * arg )
{
   sData_t * pData = (sData_t*)(arg);

   #if LED_OKBAT_FLARE_ENABLE == 0
   if( ! pData->state.bLowBatterySignal )
   {   
       // the program should not reach this point if works correctly
       return false; // stay in this routine  
   }
   #endif

   if( ! pData->state.bSignalStage )
   {
       #if LED_OKBAT_FLARE_ENABLE == 0
       pData->counters.gp_timer_counter = 0;
       pData->counters.gp_timer_maximum = LED_LOWBAT_FLARE_TIME;
       pData->state.bSignalStage = true;       
       ledRed( true );  // Turn RED LED ON
       #else
       if( pData->state.bLowBatterySignal )
       {
           pData->counters.gp_timer_counter = 0;
           pData->counters.gp_timer_maximum = LED_LOWBAT_FLARE_TIME;
           pData->state.bSignalStage = true;
           #if LED_AUTOMATALARM_COMBINED
           ledRed( !pData->state.bAutomatAlarm );  // Turn RED LED ON
           #else
           ledRed( true );  // Turn RED LED ON
           #endif
       }
       else
       {
           pData->counters.gp_timer_counter = 0;
           #if LED_AUTOMATALARM_COMBINED
           pData->counters.gp_timer_maximum = ((pData->state.bAutomatAlarm)?3:1)*LED_OKBAT_FLARE_TIME;
           #else
           pData->counters.gp_timer_maximum = LED_OKBAT_FLARE_TIME;
           #endif
           pData->state.bSignalStage = true;
           #if LED_AUTOMATALARM_COMBINED
           ledRed( false );
           #endif
           ledGreen( true );  // Turn GREEN LED ON
       }
       #endif
   }
   else   
   {
       #if LED_OKBAT_FLARE_ENABLE == 0
       pData->counters.gp_timer_counter = 0;
       pData->counters.gp_timer_maximum = LED_LOWBAT_FLARE_INTERVAL;
       pData->state.bSignalStage = false;
       ledRed( false );  // Turn RED LED OFF, and wait for next flash
       #else
       if( pData->state.bLowBatterySignal )
       {
           pData->counters.gp_timer_counter = 0;
           pData->counters.gp_timer_maximum = LED_LOWBAT_FLARE_INTERVAL;
           pData->state.bSignalStage = false;
           #if LED_AUTOMATALARM_COMBINED
           ledRed( pData->state.bAutomatAlarm );  // Turn RED LED OFF, and wait for next flash
           #else
           ledRed( false ); // Turn RED LED OFF, and wait for next flash
           #endif
       }
       else
       {
           pData->counters.gp_timer_counter = 0;
           pData->counters.gp_timer_maximum = LED_OKBAT_FLARE_INTERVAL;
           pData->state.bSignalStage = false;
           #if LED_AUTOMATALARM_COMBINED
           ledRed( pData->state.bAutomatAlarm ); 
           #endif
           ledGreen( false );  // Turn GREEN LED OFF, and wait for next flash
       }
       #endif
   } 

   return true; // let the next routine to be called
}
#endif
//------------------------------------------------------------------------------

// {seqirq_state_timer_nextstate}
/* IRQ */ static bool seqirq_state_timer_nextstate( void * arg )
{
   sData_t * pData = (sData_t*)(arg);
      
   if( pData->counters.gp_timer_counter < pData->counters.gp_timer_maximum )
   {
       pData->counters.gp_timer_counter++;
              
       return false;
   }
         
   // Useless:
   // rsa_sequence_iskip_routine( &rseq_timer );    // next timer routine
   // It is enough to return 'true'

   return true;
}

//------------------------------------------------------------------------------
#if 0 // useless
// {seqirq_state_timer_prevstate}
/* IRQ */ static bool seqirq_state_timer_prevstate( void * arg )
{
   sData_t * pData = (sData_t*)(arg);
      
   if( pData->counters.gp_timer_counter < pData->counters.gp_timer_maximum )
   {
       pData->counters.gp_timer_counter++;
              
       return false;
   }
      
   rsa_sequence_iback_routine( &rseq_timer );    // prev timer routine
   
   return false; // need to return 'false' to avoid calling next routine
}
#endif