Go Down

Topic: Which timer library to use for several seconds of delay (Read 2 times) previous topic - next topic

allanonmage

Hi there! It's been a few months since I last worked on my project, you can see my previous posts on the project here:
most recent: http://arduino.cc/forum/index.php/topic,117414.msg883388.html#msg883388
http://arduino.cc/forum/index.php/topic,116616.msg878736.html#msg878736
http://arduino.cc/forum/index.php/topic,114733.msg863416.html#msg863416

I've been reading how-to's and scouring the web and getting frustrated with what I feel is such a simple thing that I want to do.  I'm being bombarded with PWM and LEDs to the point I can't remember what it is I'm looking for (Is that all people do with arduinos???).

I want to latch an output for 2 - 10ish seconds.  The amount of time doesn't have to be very precise, but it has to fire off every time for the same amount of time.  It seems that timer interrupts will do what I want.  I understand the way they work in a general sense; the syntax I'm not sure on.  I've also found some libraries that deal with timing, but can't figure out their documentation because I have no idea what they are saying, either because I'm not famaliar with their vocab choices or i can't read their code.

What I would like from this post is a recommendation of what library to use.  I've been trying to figure out how to do this for months (off and on) and don't see the need to reinvent the wheel unless I have to.  Once I know what library will do what I want I can start to try to figure it out and/or ask any specific questions.  Right now I'm just really frustrated over all this searching and reading for it to be as clear as mud on how to proceed.

I'm using servodecode on an arduino uno, so I only have timer2 left.  What library should I use for this project?

PaulS

Quote
It seems that timer interrupts will do what I want.

It also seems like polling is going to more than accurate enough. There is no reason to drag in timers and interrupts and interrupt handlers when a simple watch and some variables is more than adequate.

Quote
What I would like from this post is a recommendation of what library to use.

I'd recommend that you forget about timers and libraries, and read up on millis().


CrossRoads

Why use a library at all?
all time related variables are unsigned long
Code: [Select]

void loop(){
if (timer_running == 0 && event1_start_trigger == 1){
event1_start_time = millis();
timer1_running = 1;
event1_end_time = event1_start_time + event1_duration;
// start timed action
}
if (timer_running == 1 && (millis() - event1_end_time ) >=0){
timer1_running = 0;
// end timed action
}
} // end void loop


Designing & building electrical circuits for over 25 years. Check out the ATMega1284P based Bobuino and other '328P & '1284P creations & offerings at  www.crossroadsfencing.com/BobuinoRev17.
Arduino for Teens available at Amazon.com.

marco_c

#3
Oct 08, 2012, 03:28 am Last Edit: Oct 08, 2012, 06:05 am by marco_c Reason: 1
For the length of time you need (a very long time for the AVR processors), the easiest way to do this is as already suggested, but here is an explanation of the logic behind how it is done.

The loop() function is called continuously - conceptually once it is completed it starts again. What you need to do is remember when you need to do something (like a calendar entry reminds you to go somewhere or be at a meeting, for eacmple) but in the case of Arduino thge 'next' time is simply when the millis() function returns a value that is greater than or equal to the target time (appointment) that you set earlier.

Interrupts are meant for situations where your code has to react NOW to fast inputs and has to do something about it. As PaulS pointed out, polling is all that is required in this case and CrossRoads has prety much set up the code needed.

The key for all this, btw, is not to create delays in the code (using the useful but misused delay() function) but keep running as fast as possible so you can get to everything in time. Multitasking is all about juggling the balls at the same time and you do this by knowing when you need to eecute something next or be fast enough to respond to an input when it happens.
Arduino libraries http://arduinocode.codeplex.com
Parola hardware & library http://parola.codeplex.com

allanonmage

#4
Oct 08, 2012, 04:16 am Last Edit: Oct 08, 2012, 04:22 am by allanonmage Reason: 1
Can you create a function inside of loop() & reference that function within loop()?  I suspect not, & I'd rather abstract these functions out of the main loop for readability & ease of use.  I'm only doing 3 things, but which I do & when are determined after a few criteria, so it might be 1 thing, it might be 1 set of 2 or it might be the other set of 2, so I'd rather not just copy/paste the code & risk a botched copy/paste later on down the road.

The loop I am using in the functions pulseOutHigh and -Low winds up being something I came up with after I realized that polling millis() didn't really work outside the main loop the same way as inside the loop.  Turns out it stops the program just like a delay(), which is not what I want to do.

Is there a way to modify pulseOutHigh and -Low allow for the polling timer?

Code: [Select]
#include <ServoDecode.h>

char * stateStrings[] = {"NOT_SYNCHED", "ACQUIRING", "READY", "in Failsafe"};
 
// Define readable non-backwards states for relays.
// This is because relays are active LOW.  That means you can ground the pin and activate the relay.
const byte relayOn = 0;
const byte relayOff = 1;

// Give each relay friendly name and map to Arduino Digital I/O pin number
const byte relay1 = 2;  // wire 1 of the engine kill actuator.  Pulse to allow engine to run.
const byte relay2 = 3;  // wire 2 of the engine kill actuator.  Pulse to kill engine.
const byte relay3 = 4;  // brake controller
const byte relay4 = 5;  // currently unused

// For reading/feedback purposes, compare to the on off constants above
byte relay1State;
byte relay2State;
byte relay3State;
byte relay4State;
                                   // seconds * milliseconds, for readibility
const unsigned long idleEngineKill = 10 * 1000; // delay to kill the motor
const unsigned long idleBrakesEngage = 2 * 1000;
const unsigned long engineStartPeriod = 15 * 1000; // length of time allowed on a new connection to allow for starting the mower engine
long whatTimeIsIt = 0;
long userInputTimer = 0;
boolean timedOut = false;

// Initialize to zero to give it an obvious out of range starting value
// valid values will be from 1000 - 2000 with maybe some spillage in either direction

int curVal[7] = {0, 0, 0, 0, 0, 0, 0}; // Current values of the channel signals
int prevVal[7] = {0, 0, 0, 0, 0, 0, 0}; // Previous values of the channel signals
int arraySize = 7;
int numChannels = 6; // size of both arrays, used for feedback purposes

const int chChangeThreshold = 15; // Used to determine if there was user input on a given channel
boolean firstTimeOn = true;
boolean firstTimeOff = true;
boolean firstInput = true;
boolean engineShouldBeOff = true;
boolean brakesShouldBeOff = false;
byte throttleChannel = 3; // sets the throttle channel to remove it from idle calculations.  Set to 0 to include all channels in idle calculations.  This is selectable as the transmitter is programmable.


void setup(){
 
 //ServoDecode setup
 ServoDecode.begin();
 ServoDecode.setFailsafe(3,1234); // set channel 3 failsafe pulse width
 
 // Relay Setup Initialize Pins so relays are inactive at reset
 digitalWrite(relay1, relayOff);
 digitalWrite(relay2, relayOff);
 digitalWrite(relay3, relayOff);
 digitalWrite(relay4, relayOff);
 relay1State = relayOff;
 relay2State = relayOff;
 relay3State = relayOff;
 relay4State = relayOff;
 
 // THEN set pins as outputs
 pinMode(relay1, OUTPUT);  
 pinMode(relay2, OUTPUT);  
 pinMode(relay3, OUTPUT);  
 pinMode(relay4, OUTPUT);    
 delay(1000); // Check that all relays are inactive at Reset
 
 // Prevent motor from starting and engage breaks
 engineKill();
 brakesEngage();

} // end setup()


void loop(){

 if(ServoDecode.getState()!= READY_state) { // transmitter is off or out of range
   if(firstTimeOff){
     engineKill();
     brakesEngage();
     whatTimeIsIt = 0;
     timedOut = false;
     firstTimeOff = false;
     firstTimeOn = true;
   } // end if(firsTimeOff)
 } // end if() transmitter not ready
 
 if(ServoDecode.getState()== READY_state) { // transmitter is on
   
   if (firstTimeOn){
     firstTimeOn = false;
     firstTimeOff = true;
     engineAllow();
     brakesEngage();
     timedOut = false;
   } //enf if() firstTimeOn
   
   if(!userInput() && !timedOut){
     if(firstInput){// test if first entry into this path
       userInputTimer = millis(); // engage a timer
       firstInput = false;
     } //end firstInput
     
     if ( (millis() >= (userInputTimer + idleBrakesEngage)) && !firstTimeOn ){ // if idleBrakesEngage seconds has passed since no user input engage the brakes
       brakesEngage();
       // userInputTimer = 0;
     } // end  
     
     if ( (millis() >= (userInputTimer + idleEngineKill)) && !firstTimeOn ){ // if idleEngineKill seconds has passed since no user input, kill the motor and engage the brakes
       engineKill();
       brakesEngage();
       userInputTimer = 0;
       firstInput = true;
       timedOut = true;
     } // end
     
   } // end if no input
   
   if(userInput()){
     userInputTimer = 0;
     firstInput = true;
     brakesDisengage();
     timedOut = false;
   } // end if() userInput()
   
 } // end if() transmitter ready
 
} // end main loop()

void engineKill(){
 
 if(relay1State == relayOn){ // just in case it was on for some random reason
   digitalWrite(relay1, relayOff); // prevents frying relays and/or actuators
   relay1State = relayOff;
 } // end safety check
 
 if(relay2State == relayOff){ // if it's already on, no need to do anything, so only do something if its off
   relay2State = relayOn;
   pulseOutLow(relay2, 500);
   relay2State = relayOff;
 } // end if()
 engineShouldBeOff = true;
 //digitalWrite(relay2, relayOn); // eventually pulse it, but for now just latch it
} // end engineKill

void engineAllow(){
 if(relay2State == relayOn){
   digitalWrite(relay2, relayOff); // just in case it was on for some random reason; prevents frying relays and/or actuators
   relay2State = relayOff;
 } // end safety check
 
 if(relay1State == relayOff){ // if it's already on, no need to do anything, so only do something if its off
   relay1State = relayOn;
   pulseOutLow(relay1, 500);
   relay1State = relayOff;
 } // end if()
 engineShouldBeOff = false;
 //digitalWrite(relay1, relayOn); // eventually pulse it, but for now just latch it
} // end instantEngineKill

void brakesEngage(){
 
 if(relay3State == relayOn){ // if it's already onn, no need to do anything, so only do something if its off
   digitalWrite(relay3, relayOff); // engages the brakes by removing power, allowing the mower to move
   relay3State = relayOff;
 } // end if()
 brakesShouldBeOff = true;
} // end brakesEngage

void brakesDisengage(){
 
 if(relay3State == relayOff){ // if it's already on, no need to do anything, so only do something if its on
   digitalWrite(relay3, relayOn); // disengages the brakes by applying voltage
   relay3State = relayOn;
 } // end if()
 brakesShouldBeOff = false;
} // end brakesDisengage

void pulseOutLow(int pin, long duration){ // pulses a digital output for a certain length of time, meant for active low relays
 unsigned long timeStart = millis();
 boolean exitThis = false;
 digitalWrite(pin, LOW);
 while(!exitThis){
   if(millis() >= (timeStart + duration)){
     digitalWrite(pin, HIGH);
     exitThis = true;
   } // end if
 } // end while()
} // end pulseOutLow

void pulseOutHigh(int pin, long duration){ // pulses a digital output for a certain length of time
 unsigned long timeStart = millis();
 boolean exitThis = false;
 digitalWrite(pin, HIGH);
 while(!exitThis){
   if(millis() >= (timeStart + duration)){
     digitalWrite(pin, LOW);
     exitThis = true;
   } // end if
 } // end while()
} // end pulseOutHIGH

boolean userInput(){ // tests for user input via the transmitter
 boolean returnVal = false;
 boolean curPrevChange[arraySize]; // used an array to test for ignore channel.  May not be needed but makes it simpler
 
 // move current values to previous array, sample current values, populate current array
 for ( int i = 1; i <= numChannels; i++ ){
   prevVal[i] = curVal[i];
   curVal[i] = ServoDecode.GetChannelPulseWidth(i); // GetChannelPulseWidth technically requires a byte, but the number won't get high enough to cause problems
 } // end move old values to proper variable
 
    // Compute if there was a change
 for ( int i = 1; i <= numChannels; i++ ){
   if(i==throttleChannel){
     curPrevChange[i] =  abs(curVal[i] - 1500) > chChangeThreshold;
     // curPrevChange[i] = false; // set to false b/c this is excluded from calculations
   } // end if      
   else{ curPrevChange[i] =  abs(curVal[i] - prevVal[i]) > chChangeThreshold;  }
        //end else
   returnVal = returnVal | curPrevChange[i]; // if anything changes, the value will go true
   } // end for() of compare channel values from each array and populate curPrevChange array
 return returnVal;
} // end userInput



Go Up