I've written a minutes/seconds countdown timer that sounds an audible alarm when the time reaches zero and has a "disarm" button that can halt the countdown. I'm going to use this for "diffuse the bomb" airsoft battles. It works as I want, but the display is a little dim. It's not bad, but I would like it to be brighter.
I know that speeding up loop() is going to get me the brightness that I'd like. Do you have any hints on streamlining the logic in loop() or have I reached the maximum reasonable brightness that one would expect when multiplexing four displays in parallel? What about alloff() ?
loop()
void loop (){
switch (digitalRead(buttonPin)){ // read disarm button state
case 0: // button is not held down.
previousMillis = millis(); // keeps millis in sync
currentMillis = previousMillis; // when button is open
case 1: // button is held down
currentMillis = millis(); // Grows away from previousMillis
// while button is held down.
}
// catches long press as soon as it crosses interval threshold.
if(currentMillis - previousMillis > interval) { // long press?
allOff(); // clear LEDS
TIMSK1=0x00; // stop timer interrupts
while ( true ){ // LEDS off unconditionally.
}
}
if (alarm == 1){ // countdown has reached zero
PORTD = B1111111; // LED states cleared.
digitalWrite(leftSecAnode,ON); // ready for new LED states
digitalWrite(rightSecAnode,ON); // ready for new LED states
digitalWrite(rightMinAnode,ON); // ready for new LED states
digitalWrite(leftMinAnode,ON); // ready for new LED states
while ( true ){ // blink G segments forever. Audible alarm comes later.
digitalWrite(G,!digitalRead(G));
delay(200);
}
}
writeMinutes();
writeSeconds();
}
Entire sketch
// Airsoft bomb utilizing timer1 interrupts
// Four digit, down timer. Able to count from 99m99s to 0m0s.
// Circuit description:
// Four 7-segment, common anode LEDS are connected in parallel.
// Multiplexing of display is achieved by switching the anodes
// on in sequence. Button input pins are tied low with 10Kohm
// resistors and go high when momentary push button (to 5V) is pressed.
// When power is applied initially, the program waits for user
// setup. Pressing the disarm/time-set button adds time, which
// is displayed on the 7 segment leds. Once the desired time has
// been set, the user presses the arm button and the timer begins
// counting down to zero.
// when the count reaches zero, the alarm goes off
// if the disarm button is depressed for more the defined
// interval, timer1 interrupt is disabled and the LEDs
// are cleared. If count reaches zero without being disarmed,
// then timer interrupt is disabled, LEDS cleared, middle LED
// segments flashes unconditionally.
// Common anode. LED on when pin is low.
// Anodes switched by PNP transistors, so
// low on the anode is also on.
// Map segments to arduino pins. Not needed once
// alarm routine uses audible alarm instead of
// blinking led segment G.
const int A = 0;
const int B = 1;
const int C = 2;
const int D = 3;
const int E = 4;
const int F = 5;
const int G = 6;
const int buttonPin = 12; // Input pin for disarm button.
int alarm = 0; // set to 1 when countdown reaches zero
long previousMillis = 0; // synced with currentMillis if button is up.
unsigned long currentMillis = 0; // grows away from previousMillis
// when button is down
long interval = 2000; // how long the button must be pressed to disarm
// Common anode;
// on when pin is low
// and off when pin is high
const int ON = LOW;
const int OFF = HIGH;
int leftSecAnode = 8; // PNP switched, so LOW = on
int rightSecAnode = 9; // PNP switched, so LOW = on
int leftMinAnode = 10; // PNP switched, so LOW = on
int rightMinAnode = 11; // PNP switched, so LOW = on
int segBytes[] = {/* seg A is byte 6. seg G is byte 0.
__ABCDEFG There is no DP or colon on my displays */
B1000000, // 0
B1111001, // 1
B0100100, // 2
B0110000, // 3
B0011001, // 4
B0010010, // 5
B0000011, // 6
B1111000, // 7
B0000000, // 8
B0010000 // 9
};
// initial state. Time must be added by user.
int minutes = 0;
int seconds = 0;
// These are set by calcDigits()
int secLeft;
int secRight;
int minLeft;
int minRight;
////////////// Timer interrupt routine /////////////////
ISR(TIMER1_OVF_vect) { // initialize timer1
TCNT1=0x0BDC; // set initial value to remove time error (16bit counter register)
if (digitalRead(buttonPin) == 0){ // halt countdown while disarm button is down
switch (seconds || minutes){
case 0: // seconds and minutes are both zero.
alarm = 1;
break;
default:
if (seconds == 0){
minutes--;
seconds = 59;
calcDigits();
break;
}
seconds--;
calcDigits();
break;
}
}
}
///////////////////////////////////////////////////
//////////////// setup () /////////////////////////
///////////////////////////////////////////////////
void setup (){
int armed = 0; // set to 1 once time to count down has been set
int armButton = 13; // press this button to start countdown
DDRD = B11111111; // PORTD pins are outputs.
PORTD = B11111111; // start with LEDS off.
pinMode(leftSecAnode,OUTPUT);
digitalWrite(leftSecAnode,OFF);
pinMode(rightSecAnode,OUTPUT);
digitalWrite(rightSecAnode,OFF);
pinMode(leftMinAnode, OUTPUT);
digitalWrite(leftMinAnode, OFF);
pinMode(rightMinAnode, OUTPUT);
digitalWrite(rightMinAnode, OFF);
pinMode(armButton, INPUT); // arm button
pinMode(buttonPin, INPUT); // time-set/disarm button
// Time must be set and bomb armed before counting down begins.
while (armed == 0){
calcDigits();
writeMinutes();
writeSeconds();
if ( digitalRead(buttonPin) == 1 ){
minutes = minutes + 1; // add one minute every time button is pressed.
delay(200); // simple debounce
}
if ( digitalRead(armButton) == 1){
armed = 1; // exits the arming loop and begins countdown.
}
}
TIMSK1=0x01; // enabled global and timer overflow interrupt;
TCCR1A = 0x00; // normal operation page 148 (mode0);
TCNT1=0x0BDC; // set initial value to remove time error (16bit counter register)
TCCR1B = 0x04; // start timer/ set clock
}
///////////////////////////////////////////////////
/////////////////// loop() ////////////////////////
///////////////////////////////////////////////////
void loop (){
switch (digitalRead(buttonPin)){ // read disarm button state
case 0: // button is not held down.
previousMillis = millis(); // keeps millis in sync
currentMillis = previousMillis; // when button is open
case 1: // button is held down
currentMillis = millis(); // Grows away from previousMillis
// while button is held down.
}
// catches long press as soon as it crosses interval threshold.
if(currentMillis - previousMillis > interval) { // long press?
allOff(); // clear LEDS
TIMSK1=0x00; // stop timer interrupts
while ( true ){ // LEDS off unconditionally.
}
}
if (alarm == 1){ // countdown has reached zero
PORTD = B1111111; // LED states cleared.
digitalWrite(leftSecAnode,ON); // ready for new LED states
digitalWrite(rightSecAnode,ON); // ready for new LED states
digitalWrite(rightMinAnode,ON); // ready for new LED states
digitalWrite(leftMinAnode,ON); // ready for new LED states
while ( true ){ // blink G segments forever. Audible alarm comes later.
digitalWrite(G,!digitalRead(G));
delay(200);
}
}
writeMinutes();
writeSeconds();
}
//////// Function definitions: ///////////////
void allOff(){
digitalWrite(rightSecAnode,OFF);
digitalWrite(leftSecAnode,OFF);
digitalWrite(rightMinAnode,OFF);
digitalWrite(leftMinAnode,OFF);
}
void writeMinutes(){
PORTD = segBytes[minLeft]; // display left digit
digitalWrite(leftMinAnode, ON);
allOff();
PORTD = segBytes[minRight]; // display right digit
digitalWrite(rightMinAnode, ON);
allOff();
}
void writeSeconds(){
PORTD = segBytes[secLeft]; // display left digit
digitalWrite(leftSecAnode, ON);
allOff();
PORTD = segBytes[secRight]; // display right digit
digitalWrite(rightSecAnode, ON);
allOff();
}
void calcDigits(){
secLeft = seconds / 10; // left digit
secRight = seconds % 10; // right digit
minLeft = minutes / 10; // left digit
minRight = minutes % 10; // right digit
}