-
When you write sketches, always write them in a non-blocking fashion.
i.e. we never use delay( ) as it stops normal program execution for the delay period.
When delay( ) is used the sketch becomes unresponsive and appears to stutter.
Suggest you not use while( ) either unless you are sure it will quickly execute.
-
This sketch uses the millis( ) / micros( ) functions to create non-blocking delays or TIMERs.
We call this technique Blink Without Delay.
-
This sketch goes one step further, we create a structure makeTIMER to automate TIMER creation.
-
A structure or a class is used to define a object.
Objects contain built-in variables and functions.
-
We will not cover writing of a structure or class, however, we will use them to create TIMERs for our sketches.
-
In this sketch we can define all the TIMERs we need as seen in the example below.
Below we make a TIMER called heartbeatTIMER.
- It times in milliseconds
- It expires in 500ms
- At power up time it is ENABLED
- It will reset and run again and again.
//========================
makeTIMER heartbeatTIMER =
{
MILLIS, 0, 500ul, ENABLED, YES //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};
- We have access to several TIMER functions.
In our makeTIMER structure, where XXX is the name of our TIMER, we can use:
XXX.checkTIMER(); Checks to see if the TIMER has expired
XXX.enableRestartTIMER(); Enables and restarts our TIMER
XXX.disableTIMER(); Disables our TIMER
XXX.expireTimer(); Makes our TIMER expire before it should
XXX.setInterval(????); Allows us to set a new TIMER interval
- See the code section below.
- Here we ask the question “has this TIMER expired”.
- If it has expired, we toggle the heartbeat LED, then continue to execute our code.
- When the TIMER does expire, the TIMER automatically restarts as that’s the way we defined it to operate.
Hence the operation repeats again and again.
- If the TIMER has not expired, we do not toggle the LED.
//======================================================================== T I M E R heartbeatLED
//is it time to toggle the heartbeat LED ?
if (heartbeatTIMER.checkTIMER() == EXPIRED)
{
//toggle the heartbeat LED
digitalWrite(heartbeatLED, digitalRead(heartbeatLED) == HIGH ? LOW : HIGH);
}
-
In the sketch below, modify the heartbeat TIMER definition so the heartbeat LED toggles every 1 second.
-
Does the heartbeat LED now toggle every 1 second ?
-
As we progress, ask questions about things you do not understand.
//
//================================================^================================================
// S u r f C o m p e t i t i o n T i m e r
//
// https://forum.arduino.cc/t/newbie-building-a-surf-comp-timer-does-my-code-look-ok/1358758/1
//
// Name
//
// Version YY/MM/DD Comments
// ======= ======== ========================================================================
// 1.00 25/03/01 Running code
//
//
//
//
// For wiring connections, see the T i m e r S u r f S c h e m a t i c
//
//
// Sketch Notes:
// Surf Competition Timer
// - 2 rotary switches control the timer function (off/Run/Pause) and (timer set 20,25,30,35,40 minutes)
// - when timer starts, hooter sounds for 3 seconds, green light ON and timer counts down (no delays)
// - when timer reaches 5 minute mark - Green light OFF and Amber light ON
// - when timer reaches 0 - amber light OFF, red light ON, hooter sounds (high 3 seconds, low 1 second, high 3 Seconds, Low)
// - when timer reaches 0 a 30 second reset time starts ( time frame between Heats)
// a 30 second countdown is seen on the TM1637
// - when 30 second reset timer reaches 0, red light OFF, the main timer restarts - hooter, green light etc.
//
// - once timers starts the selected time can not change unless timer is turned Off/Reset.
// - if pause is selected - count down time paused, hooter sounds twice and red light on.
// - if un paused, timer resumes from current state, red light goes OFF, hooter sounds for 3 seconds
#include <TM1637Display.h>
//TM1637 Notes:
//reference: https://github.com/avishorp/TM1637/blob/master/TM1637Display.h
// showNumberDecEx(int num, uint8_t dots = 0, bool leading_zero = false, uint8_t length = 4, uint8_t pos = 0);
//================================================
#define colon 0b01000000
#define LEDon HIGH //PIN---[220R]---A[LED]K---GND
#define LEDoff LOW
#define PRESSED LOW //+5V---[Internal 50k]---PIN---[Switch]---GND
#define RELEASED HIGH
#define CLOSED LOW //+5V---[Internal 50k]---PIN---[Switch]---GND
#define OPENED HIGH
#define relayON HIGH
#define relayOFF LOW
#define ENABLED true
#define DISABLED false
#define MAXIMUM 180
#define MINIMUM 0
// millis() / micros() B a s e d T I M E R S
//================================================^================================================
//To keep the sketch tidy, you can put this structure in a different tab in the IDE
//
//These TIMER objects are non-blocking
struct makeTIMER
{
#define MILLIS 0
#define MICROS 1
#define ENABLED true
#define DISABLED false
#define YES true
#define NO false
#define STILLtiming 0
#define EXPIRED 1
#define TIMERdisabled 2
//these are the bare minimum "members" needed when defining a TIMER
byte TimerType; //what kind of TIMER is this? MILLIS/MICROS
unsigned long Time; //when the TIMER started
unsigned long Interval; //delay time which we are looking for
bool TimerFlag; //is the TIMER enabled ? ENABLED/DISABLED
bool Restart; //do we restart this TIMER ? YES/NO
//================================================
//condition returned: STILLtiming (0), EXPIRED (1) or TIMERdisabled (2)
//function to check the state of our TIMER ex: if(myTimer.checkTIMER() == EXPIRED);
byte checkTIMER()
{
//========================
//is this TIMER enabled ?
if (TimerFlag == ENABLED)
{
//========================
//has this TIMER expired ?
if (getTime() - Time >= Interval)
{
//========================
//should this TIMER restart again?
if (Restart == YES)
{
//restart this TIMER
Time = getTime();
}
//this TIMER has expired
return EXPIRED;
}
//========================
else
{
//this TIMER has not expired
return STILLtiming;
}
} //END of if (TimerFlag == ENABLED)
//========================
else
{
//this TIMER is disabled
return TIMERdisabled;
}
} //END of checkTime()
//================================================
//function to enable and restart this TIMER ex: myTimer.enableRestartTIMER();
void enableRestartTIMER()
{
TimerFlag = ENABLED;
//restart this TIMER
Time = getTime();
} //END of enableRestartTIMER()
//================================================
//function to disable this TIMER ex: myTimer.disableTIMER();
void disableTIMER()
{
TimerFlag = DISABLED;
} //END of disableTIMER()
//================================================
//function to restart this TIMER ex: myTimer.restartTIMER();
void restartTIMER()
{
Time = getTime();
} //END of restartTIMER()
//================================================
//function to force this TIMER to expire ex: myTimer.expireTimer();
void expireTimer()
{
//force this TIMER to expire
Time = getTime() - Interval;
} //END of expireTimer()
//================================================
//function to set the Interval for this TIMER ex: myTimer.setInterval(100);
void setInterval(unsigned long value)
{
//set the Interval
Interval = value;
} //END of setInterval()
//================================================
//function to return the current time
unsigned long getTime()
{
//return the time i.e. millis() or micros()
//========================
if (TimerType == MILLIS)
{
return millis();
}
//========================
else
{
return micros();
}
} //END of getTime()
}; //END of struct makeTIMER
// D e f i n e a l l o u r T I M E R S
//================================================^================================================
/*example
//========================
makeTIMER toggleLED =
{
MILLIS/MICROS, 0, 500ul, ENABLED/DISABLED, YES/NO //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};
TIMER functions we can access:
toggleLED.checkTIMER();
toggleLED.enableRestartTIMER();
toggleLED.disableTIMER();
toggleLED.expireTimer();
toggleLED.setInterval(100ul);
*/
//========================
makeTIMER heartbeatTIMER =
{
MILLIS, 0, 500ul, ENABLED, YES //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};
//========================
makeTIMER switchesTIMER =
{
MILLIS, 0, 50ul, ENABLED, YES //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};
//========================
makeTIMER machineTIMER =
{
MICROS, 0, 1000ul, ENABLED, YES //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};
//========================
makeTIMER countDownTIMER =
{
MILLIS, 0, 1000ul, DISABLED, YES //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};
//========================
makeTIMER hooterTIMER =
{
MILLIS, 0, 1000ul, DISABLED, YES //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};
//========================
makeTIMER heatTIMER =
{
MILLIS, 0, 1000ul, DISABLED, YES //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};
// S t a t e M a c h i n e s
//================================================^================================================
//
//========================
//states in our countdown machine
enum STATES : byte
{
POWERUP, RESET, START, RUNNING, RESTARTtiming, HOOTERsequence, WAITING, PAUSED, FINISHED
};
STATES mState = POWERUP; //mState is machine state
//========================
//states in our hooter sequence machine
enum {sequenceStart = 0, sequence1, sequence2, sequence3, sequence4, sequenceTiming};
byte sequenceState = 0;
byte lastSequenceState = 0;
// G P I O s A n d V a r i a b l e s
//================================================^================================================
//
//GPIOs
//================================================
//
//5 position switch
const byte switch1 = 2;
const byte switch2 = 3;
const byte switch3 = 4;
const byte switch4 = 5;
const byte switch5 = 6;
//TM1637 display module
const byte CLK_PIN = 7;
const byte DIO_PIN = 8;
//relays
const byte RedLight = 9;
const byte AmberLight = 10;
const byte GreenLight = 11;
const byte Hooter = 12;
const byte heartbeatLED = 13;
//3 position switch
const byte RESET_PIN = 14; //A0
const byte START_PIN = 15; //A1
const byte PAUSE_PIN = 16; //A2
//create display object
TM1637Display display(CLK_PIN, DIO_PIN);
//VARIABLES
//================================================
//
bool restartFlag = DISABLED; //when ENABLED, the next "Heat" will start in 30 seconds
//const byte Time1 = 20 * 60; //seconds
const unsigned int Time1 = 40; //40 seconds for testing <---------------<<<<<<
const unsigned int Time2 = 25 * 60; //seconds
const unsigned int Time3 = 30 * 60; //seconds
const unsigned int Time4 = 35 * 60; //seconds
const unsigned int Time5 = 40 * 60; //seconds
unsigned int timeSelected;
byte lastSwitch1 = OPENED;
byte lastSwitch2 = OPENED;
byte lastSwitch3 = OPENED;
byte lastSwitch4 = OPENED;
byte lastSwitch5 = OPENED;
byte lastRESET_PIN = OPENED;
byte lastSTART_PIN = OPENED;
byte lastPAUSE_PIN = OPENED;
//timing stuff
const unsigned int GreenOffTime = 5 * 60; //seconds
const unsigned int nextHeatTime = 30; //seconds
const unsigned int hooterOnTime = 3 * 1000; //3000 milliseconds
unsigned int heatRestartCounter;
unsigned int heatCounter ;
// s e t u p ( )
//================================================^================================================
//
void setup()
{
Serial.begin(115200);
//wait for the serial
while (!Serial);
//========================
//internal pull-ups are turned on
pinMode(switch1, INPUT_PULLUP);
pinMode(switch2, INPUT_PULLUP);
pinMode(switch3, INPUT_PULLUP);
pinMode(switch4, INPUT_PULLUP);
pinMode(switch5, INPUT_PULLUP);
pinMode(RESET_PIN, INPUT_PULLUP);
pinMode(START_PIN, INPUT_PULLUP);
pinMode(PAUSE_PIN, INPUT_PULLUP);
//========================
//relays are off at power up time
digitalWrite(RedLight, relayOFF);
pinMode(RedLight, OUTPUT);
digitalWrite(AmberLight, relayOFF);
pinMode(AmberLight, OUTPUT);
digitalWrite(GreenLight, relayOFF);
pinMode(GreenLight, OUTPUT);
digitalWrite(Hooter, relayOFF);
pinMode(Hooter, OUTPUT);
digitalWrite(heartbeatLED, LEDoff);
pinMode(heartbeatLED, OUTPUT);
//========================
display.setBrightness(7); //display brightness (0-7)
display.clear();
updateTM1637(0);
} //END of setup()
// l o o p ( )
//================================================^================================================
//
void loop()
{
//======================================================================== T I M E R heartbeatLED
//is it time to toggle the heartbeat LED ?
if (heartbeatTIMER.checkTIMER() == EXPIRED)
{
//toggle the heartbeat LED
digitalWrite(heartbeatLED, digitalRead(heartbeatLED) == HIGH ? LOW : HIGH);
}
//======================================================================== T I M E R switches
//is it time to check our switches ?
if (switchesTIMER.checkTIMER() == EXPIRED)
{
checkSwitches();
}
//======================================================================== T I M E R machine
//is it time to service our State Machine ?
if (machineTIMER.checkTIMER() == EXPIRED)
{
checkMachine();
}
//================================================
//other non blocking code goes here
//================================================
} //END of loop()
// c h e c k M a c h i n e ( )
//================================================^================================================
//
void checkMachine()
{
} //END of checkMachine()
// c h e c k S w i t c h e s ( )
//================================================^================================================
//
void checkSwitches()
{
} //END of checkSwitches()
// u p d a t e T M 1 6 3 7 ( )
//================================================^================================================
//
void updateTM1637(unsigned int counter)
{
unsigned int minutes = counter / 60;
unsigned int seconds = counter % 60;
unsigned int num = minutes * 100 + seconds;
//update TM1637
// num, dots, leading 0s, digits, LSD position
display.showNumberDecEx(num, colon, true, 4, 0);
} //END of updateTM1637()
//================================================^================================================