Timer interrupt to achieve trip timing

Dear all ,

I wanted to implement with arduino uno
I need simple favor implementing this code in timer interrupt.
I have pdf attached for reference.
I have overload trip of motor setting 30A with Trip Time 30S as example . i would like to achieve attached curve using timer interrupt. can someone guide me how it can be implemented.

user can change over loadtrip and timing accordingly.

curve is meant to achieve below function
Lower current higher trip time and higher current lower trip time

TIMING.pdf (17.9 KB)

AJITnayak:
Dear all ,

I wanted to implement with arduino uno
I need simple favor implementing this code in timer interrupt.
I have pdf attached for reference.
I have overload trip of motor setting 30A with Trip Time 30S as example . i would like to achieve attached curve using timer interrupt. can someone guide me how it can be implemented.

user can change over loadtrip and timing accordingly.

curve is meant to achieve below function
Lower current higher trip time and higher current lower trip time

Not that I would recommend it, but it looks to me something you can implement with millis!

read the current and look up the correct ponding time (or calculate it) and use that as you "time to trip"!

Why not just use a SAFE motor breaker!

Sorry i missed some part. Actually i need to take percentage of overload set current.
In example overload trip time is 30s and overload_trip_current is 30A
while getting trip (overload_trip_current *30%)+overload_trip_current
in chart ,
if actual nominal current is % value of overload_trip_current corresponding trip time to be selected. Let me know how it can be achieved in code with timer interrupt

img1.JPG is just a screen capture of the spreadsheet shown in TIMING.pdf.

Your chart and your graph don't agree. The time scale is reversed in the graph.

In what universe does it make sense for a motor capable of drawing up to 30A to get shut down after 30 seconds at 1 Amp? If you extend the graph for another second and another Amp you will have the motor shutting down in 31 seconds if it doesn't draw any current at all.

It looks like you don't understand the design requirements. Perhaps the motor can stand 30A indefinitely but 100% overcurrent (60A) trips immediately and 50% overcurrent (45A) trips in 15 seconds (half the 30-second range). That would have 33A (10% overcurrent) tripping in about 27 seconds (10% under the 30-second limit).

In what universe does it make sense for a motor capable of drawing up to 30A to get shut down after 30 seconds at 1 Amp? If you extend the graph for another second and another Amp you will have the motor shutting down in 31 seconds if it doesn't draw any current at all.

Please refer image carefully.

Those are set value not the trip value.

i given sample example.

overload trip time is 30s
overload current limit 30A

In next column i have drive time vs adjustment of curve.
where 30A current takes 1s to trip time and 1A current takes 30S
actual data pdf not updated.

actually its wrong in method . correct method as below

if nominal current > ((overload_trip_current_limit *30%)+overload_trip_current_lmit ) then trip time should be low

if nominal current > ((overload_trip_current_limit *1%)+overload_trip_current_limit ) then trip time should be high

i wanted to achieve this in timer interrupt .

Again I do not really using uC at a trip circuit is really SAFE... however looking at your approach I think it's not quite right. (btw its all so attempts to explain I find are very confusing!)

Think of it this way:
uC is running very quickly so it will also be taking read samples very quicly

What I mean it that in your main loop you should be constantly doing a read and the moment it detected its about your MINIMUM trip limit, it starts the trip timer the based on the read and your presets but also on the the subsequent readings. (time interval may need to decrease further if reading keeps increasing or conversely increase the time interval if decreasing).

once the time interval is reached, your circuit trips.

you could use a timer interrupt but is would be messy I think (simpler to use millis() I should think)

Here's a shot at what I think you're describing. I'm using a 1mS compare interrupt to tick an integrator whenever an overload is seen. The higher the overload, the faster the integrator rises to a limit point at which time the logic opens a relay and inserts a 2-minute cooling time before re-closing the relay.

I don't know how you plan to measure motor current so I just put in an analogRead() and an arbitrary K (constant) to placehold the actual equations and methods.

I assumed a 30A point at which an overcurrent is detected, a 1-second trip at 30%-high and ~30-sec trip
at 1% over the overload limit. These numbers may be approximate. See if it's any use.

#define CURRSNSE_PIN        (int)A0
#define RELAY_COIL          (int)13

#define CLOSE_RELAY         HIGH
#define OPEN_RELAY          LOW

#define OL_INTEGRATOR_LIM   300000U     //integrator upper limit
#define COOL_OFF_TIME       120000U     //2-minute cool down time

//#define DEBUG               1
#define TEST_CURRENT        (float)30.3



#define OVERLOAD_CURRENT    (float)30.0 //Amps

#define CURR_CHK_INTERVAL   50          //mS current read interval

//this is a fake conversion from ADC counts to float amps
//correct for whatever sensor you're using
#define K_VAL               (float)0.04887 

unsigned long
    intgrlBreakerTrip,
    integratorAdder;

int
    OveragePct,
    nMotorCurrent;
float
    fMotorCurrent;
bool
    bTrippedCoolingFlag;
unsigned long
    timeCooling,
    timeCheckCurrent,
    timeNow;
    //timebreakerTrip;

void setup() 
{
    Serial.begin(9600);
    while(!Serial);
    
    //setup a compare interrupt that will happen
    //at 1mS intervals (uses Timer0, like millis)
    OCR0A = 0x80;
    TIMSK0 |= _BV(OCIE0A);

    pinMode( RELAY_COIL, OUTPUT );
    digitalWrite( RELAY_COIL, CLOSE_RELAY );
    
    pinMode( CURRSNSE_PIN, INPUT );

    timeNow = millis();
    timeCheckCurrent = timeNow;
    bTrippedCoolingFlag = false;
    intgrlBreakerTrip = 0;
    integratorAdder = 0;
    
}//setup

void loop() 
{
    CheckCurrent();

}//loop

/////////////////////////////////////////////////////////////////
//  CheckCurrent
//      -   breaker action done with numerical integration
//      -   a counter starts at zero and ticks up once every
//          mS
//  NOTES:
//      - assume start integration from 0
//      - overload of 30% is seen
//          - desired trip time of 1-second
//          - 300 counts/mS gives 300000 ticks in 1-sec
//          
//      - an overload 1% is seen
//          - desire to trip in 30-seconds
//          - 10 counts/mS gives 300000 ticks in 30-sec
//
//  Integrator sum term is basically 10 x %overload
//
/////////////////////////////////////////////////////////////////
void CheckCurrent( void )
{
    //check the current every 50mS
    //integrator ticks up at 1mS
    timeNow = millis();
    if( (timeNow - timeCheckCurrent) < CURR_CHK_INTERVAL )
        return;

    timeCheckCurrent = timeNow;

    //read ADC counts and convert to amps
    nMotorCurrent = analogRead( CURRSNSE_PIN );
    fMotorCurrent = (float)nMotorCurrent * K_VAL;   //made-up equation    

    //if we've below the threshold, reset the integrator
    if( fMotorCurrent < OVERLOAD_CURRENT )
    {
        SetBreakerTripIntegral(0);
        SetintegratorAdder(0);
            
    }//if
    else
    {
        //update the size of the additive term to the integrator
        OveragePct = (int)((((fMotorCurrent/OVERLOAD_CURRENT)-1.0)*100.0)+0.5);
        //Serial.println( OveragePct );
        if( OveragePct > 30 )
            OveragePct = 30;
            
        //and update it
        SetintegratorAdder(OveragePct * 10);

    }//else

    CheckOverload();

}//CheckCurrent

void CheckOverload( void )
{
    //check to see if the integrator has reached the breaker-point
    if( !bTrippedCoolingFlag )
    {
        //check to see if the integrator has reached the breaker-point
        if( GetBreakerTripTime() >= OL_INTEGRATOR_LIM )
        {
            //yes; open relay & set cooldown timer
            bTrippedCoolingFlag = true;
            digitalWrite( RELAY_COIL, OPEN_RELAY );
            timeCooling = millis();
        
        }//if
        
    }//if
    else
    {
        //here we're timing the post-trip cooldown time
        if( (millis() - timeCooling) >= COOL_OFF_TIME )
        {
            //when timing done, re-enable the relay
            //with a reset of the integral
            bTrippedCoolingFlag = false;
            digitalWrite( RELAY_COIL, CLOSE_RELAY );
            SetBreakerTripIntegral(0);
            SetintegratorAdder(0);
            
        }//if
        
    }//else
    
}//CheckOverload


//these provide relatively safe ways to read and write ISR variables
void SetintegratorAdder( long val )
{
    noInterrupts();
    integratorAdder = val;
    interrupts();
    
}//SetIntegratorAdder

void SetBreakerTripIntegral( unsigned long val )
{
    noInterrupts();
    intgrlBreakerTrip = val;
    interrupts();
    
}//SetBreakerTripIntegral


unsigned long GetBreakerTripTime( void  )
{
    unsigned long
        tmp;
        
    noInterrupts();
    tmp = intgrlBreakerTrip;
    interrupts();
    
    return( tmp );
    
}//GetBreakerTripTime

//mS compare interrupt handler
SIGNAL( TIMER0_COMPA_vect ) 
{    
    //on each tick, simply add the adder to the integrator
    intgrlBreakerTrip += integratorAdder;

    //if it maxes out against the limit, force it to the limit
    if( intgrlBreakerTrip >= OL_INTEGRATOR_LIM )
        intgrlBreakerTrip = OL_INTEGRATOR_LIM;
                    
}//ISR handler

integratorAdder is not adequately protected (assigned zero in CheckCurrent which is called from loop). It should be volatile.

intgrlBreakerTrip should be volatile.

#define OL_INTEGRATOR_LIM   300000L     //integrator upper limit

...the correct datatype is unsigned long / UL.

#define COOL_OFF_TIME       120000L     //2-minute cool down time

...ditto.

[quote author=Coding Badly link=msg=4024478 date=1547882944]
integratorAdder is not adequately protected (assigned zero in CheckCurrent which is called from loop). It should be volatile.

intgrlBreakerTrip should be volatile.[/quote]

Thanks for catching the direct alteration of integratorAddr outside the interrupt. I did consider volatile but decided instead to do some small functions to modify them. In the instance(s) where I modified them directly, that was definitely a mistake.

Here's the function for changing integratorAdder:

//these provide relatively safe ways to read and write ISR variables
void SetintegratorAdder( long val )
{
    noInterrupts();
    integratorAdder = val;
    interrupts();
    
}//SetIntegratorAdder

The Arduino reference (https://www.arduino.cc/reference/en/language/variables/variable-scope--qualifiers/volatile/) suggests that volatile may not be sufficient for variables larger in type than byte (like ints or longs) and that shutting off interrupts is one of the safer ways to do it.

Blackfin:
Thanks for catching...

You are welcome.

Blackfin:
I did consider volatile but decided instead to do some small functions to modify them.

Not marking them volatile strikes me as foolish.

If you do mark them volatile the interrupt service routine should be changed to use temporary locals.