Swim timer code listing

To use this code just copy and paste it directly into a new Arduino sketch in the Arduino software. You’ll need to have an ATmega328 with a pre-loaded bootloader. Select the board type that corresponds to the bootloader type on your ATmega328. A wealth of information and help can be found at Adafruit on using the Arduino software and hardware. Adafruit is also a great source for buying Arduino products.

/*
Code for swim timer with 4 digit LED display
Common Anode
Includes programmable delay
Adding total time or a reset for timing sets (lap timer)
High on lapPin means total time display
Low on lapPin resets lap time (when it first transitions)
In this version I'm using an 8MHz crystal but haven't reprogrammed
the '328 so I need to make one second = 500msec (justs one place)
Need to change crystal to 16MHz or pull '328 to reprogram
I've also remapping the pins to match the new prototype
*/

int timeOn = 1; // msec on time for each segment;
unsigned long zeroTime; //Power on zero time
unsigned long timeSeconds; //Current systeme time
unsigned long zeroRef; // Time currently being used for zero
int seconds;
int tenSeconds;
int minutes;
int tenMinutes;
boolean counterRestartTrigger= true;

//pin assignments on arduino
const int Anode1 = 4; //Common anode for digit 1
const int Anode2 = 9;
const int Anode3 = 10;
const int Anode4 = 12;
const int SegA = 3; // Segment A cathode. Top, then clockwise, center last
const int SegB = 11;
const int SegC = 8;
const int SegD = 6;
const int SegE = 5;
const int SegF = 2;
const int SegG = 13;
const int dp = 7; // Decimal point. Use only on digit 2
const int lapPin = 14; //digital input on A0 pin

const byte zero = B11111100; //7-segment zero, etc
const byte one =  B01100000;
const byte two =  B11011010;
const byte three = B11110010;
const byte four = B01100110;
const byte five = B10110110;
const byte six =  B10111110;
const byte seven = B11100000;
const byte eight = B11111110;
const byte nine = B11110110;
const byte none = B00000000; // use for suppressed zero

byte digit1; // tens of minutes digit
byte digit2; // minutes digit
byte digit3; // tens of seconds digit
byte digit4; // seconds digit

// translates the digit value to the 7 segment encoding;
byte getDigit(int digit){
byte Rdigit;
switch(digit){
case 0:
Rdigit = zero;
break;
case 1:
Rdigit = one;
break;
case 2:
Rdigit = two;
break;
case 3:
Rdigit = three;
break;
case 4:
Rdigit = four;
break;
case 5:
Rdigit = five;
break;
case 6:
Rdigit = six;
break;
case 7:
Rdigit = seven;
break;
case 8:
Rdigit = eight;
break;
case 9:
Rdigit = nine;
break;
}
return Rdigit;
}

void setup() {
// initialize the digital output pins
// Anode pins have 220 Ohms in series to load
pinMode(Anode1, OUTPUT);   //Digit 1 common anode
pinMode(Anode2, OUTPUT);   //Digit 2 common anode
pinMode(Anode3, OUTPUT);  //Digit 3 common anode
pinMode(Anode4, OUTPUT);  //Digit 4 common anode
pinMode(SegA, OUTPUT);
pinMode(SegB, OUTPUT);
pinMode(SegC, OUTPUT);
pinMode(SegD, OUTPUT);
pinMode(SegE, OUTPUT);
pinMode(SegF, OUTPUT);
pinMode(SegG, OUTPUT);
pinMode(dp, OUTPUT);     //Decimal point
pinMode(lapPin, INPUT); // lap pin is an input
// turn display off
digitalWrite(Anode1,LOW); // A low state keeps the segments off
digitalWrite(Anode2,LOW);
digitalWrite(Anode3,LOW);
digitalWrite(Anode4,LOW);

digitalWrite(SegA, HIGH);  //These are the cathodes
digitalWrite(SegB, HIGH);  //A high state keeps them off
digitalWrite(SegC, HIGH);
digitalWrite(SegD, HIGH);
digitalWrite(SegE, HIGH);
digitalWrite(SegF, HIGH);
digitalWrite(SegG, HIGH);
digitalWrite(dp, HIGH);

// Get the system time to set zero
zeroTime = millis();
zeroRef = zeroTime; // zero reference for total time
}

void loop() {
//see if the timing mode is lap or total time
if(digitalRead(lapPin)== 1) { //display total time
counterRestartTrigger = true; // set restart trigger
zeroRef = zeroTime;
}else {                    // lapPin = 0, start lap time if just transitioned
if(counterRestartTrigger == true) { // Just transitionsed so restart
zeroRef = millis();    // reset zero reference
counterRestartTrigger = false; // reset restart trigger
}
}

//get the system time
//  timeSeconds =( millis() - zeroRef)/1000; // 16 MHz clock
timeSeconds =( millis() - zeroRef)/500; // 8 MHz clock not programmed
tenMinutes = (timeSeconds % 6000) / 600;
minutes = (timeSeconds % 600) / 60;
tenSeconds = timeSeconds % 60 / 10;
seconds = timeSeconds % 10;
digit1 = getDigit(tenMinutes);
digit2 = getDigit(minutes);
digit3 = getDigit(tenSeconds);
digit4 = getDigit(seconds);

// zero suppression for minutes
if( digit1 == zero){
digit1 = none;
if(digit2 == zero) digit2 = none;
}

// segment A
if(bitRead(digit1, 7)==1){   // setup anodes on or off for each digit
digitalWrite(Anode1,HIGH); // Starting with segment A as bit 7
}else{
digitalWrite(Anode1,LOW);
}
if(bitRead(digit2, 7)==1){
digitalWrite(Anode2,HIGH);
}else{
digitalWrite(Anode2,LOW);
}
if(bitRead(digit3, 7)==1){
digitalWrite(Anode3,HIGH);
}else{
digitalWrite(Anode3,LOW);
}
if(bitRead(digit4, 7)==1){
digitalWrite(Anode4,HIGH);
}else{
digitalWrite(Anode4,LOW);
}
digitalWrite(SegA, LOW);     // enable segment
delay(timeOn);
digitalWrite(SegA, HIGH);    // then turn off

// segment B
if(bitRead(digit1, 6)==1){
digitalWrite(Anode1,HIGH);
}else{
digitalWrite(Anode1,LOW);
}
if(bitRead(digit2, 6)==1){
digitalWrite(Anode2,HIGH);
}else{
digitalWrite(Anode2,LOW);
}
if(bitRead(digit3, 6)==1){
digitalWrite(Anode3,HIGH);
}else{
digitalWrite(Anode3,LOW);
}
if(bitRead(digit4, 6)==1){
digitalWrite(Anode4,HIGH);
}else{
digitalWrite(Anode4,LOW);
}
digitalWrite(SegB, LOW);
delay(timeOn);
digitalWrite(SegB, HIGH);

//segment C
if(bitRead(digit1, 5)==1){
digitalWrite(Anode1,HIGH);
}else{
digitalWrite(Anode1,LOW);
}
if(bitRead(digit2, 5)==1){
digitalWrite(Anode2,HIGH);
}else{
digitalWrite(Anode2,LOW);
}
if(bitRead(digit3, 5)==1){
digitalWrite(Anode3,HIGH);
}else{
digitalWrite(Anode3,LOW);
}
if(bitRead(digit4, 5)==1){
digitalWrite(Anode4,HIGH);
}else{
digitalWrite(Anode4,LOW);
}
digitalWrite(SegC, LOW);
delay(timeOn);
digitalWrite(SegC, HIGH);

//Segment D
if(bitRead(digit1, 4)==1){
digitalWrite(Anode1,HIGH);
}else{
digitalWrite(Anode1,LOW);
}
if(bitRead(digit2, 4)==1){
digitalWrite(Anode2,HIGH);
}else{
digitalWrite(Anode2,LOW);
}
if(bitRead(digit3, 4)==1){
digitalWrite(Anode3,HIGH);
}else{
digitalWrite(Anode3,LOW);
}
if(bitRead(digit4, 4)==1){
digitalWrite(Anode4,HIGH);
}else{
digitalWrite(Anode4,LOW);
}
digitalWrite(SegD, LOW);
delay(timeOn);
digitalWrite(SegD, HIGH);

// Segment E
if(bitRead(digit1, 3)==1){
digitalWrite(Anode1,HIGH);
}else{
digitalWrite(Anode1,LOW);
}
if(bitRead(digit2, 3)==1){
digitalWrite(Anode2,HIGH);
}else{
digitalWrite(Anode2,LOW);
}
if(bitRead(digit3, 3)==1){
digitalWrite(Anode3,HIGH);
}else{
digitalWrite(Anode3,LOW);
}
if(bitRead(digit4, 3)==1){
digitalWrite(Anode4,HIGH);
}else{
digitalWrite(Anode4,LOW);
}
digitalWrite(SegE, LOW);
delay(timeOn);
digitalWrite(SegE, HIGH);

//Segment F
if(bitRead(digit1, 2)==1){
digitalWrite(Anode1,HIGH);
}else{
digitalWrite(Anode1,LOW);
}
if(bitRead(digit2, 2)==1){
digitalWrite(Anode2,HIGH);
}else{
digitalWrite(Anode2,LOW);
}
if(bitRead(digit3, 2)==1){
digitalWrite(Anode3,HIGH);
}else{
digitalWrite(Anode3,LOW);
}
if(bitRead(digit4, 2)==1){
digitalWrite(Anode4,HIGH);
}else{
digitalWrite(Anode4,LOW);
}
digitalWrite(SegF, LOW);
delay(timeOn);
digitalWrite(SegF, HIGH);

//Segment G
if(bitRead(digit1, 1)==1){
digitalWrite(Anode1,HIGH);
}else{
digitalWrite(Anode1,LOW);
}
if(bitRead(digit2, 1)==1){
digitalWrite(Anode2,HIGH);
}else{
digitalWrite(Anode2,LOW);
}
if(bitRead(digit3, 1)==1){
digitalWrite(Anode3,HIGH);
}else{
digitalWrite(Anode3,LOW);
}
if(bitRead(digit4, 1)==1){
digitalWrite(Anode4,HIGH);
}else{
digitalWrite(Anode4,LOW);
}
digitalWrite(SegG, LOW);
delay(timeOn);
digitalWrite(SegG, HIGH);

// colon
// the decimal points on digit 2 and 3 form the colon
// First set off anodes low
digitalWrite(Anode1,LOW);
digitalWrite(Anode2,HIGH);
digitalWrite(Anode3,LOW);// Sometimes high for colon (depends on LED)
digitalWrite(Anode4,LOW);
digitalWrite(dp, LOW);
delay(timeOn);
digitalWrite(dp, HIGH);
digitalWrite(Anode1,LOW); // Back to off
digitalWrite(Anode2,LOW);
digitalWrite(Anode3,LOW);
digitalWrite(Anode4,LOW);
}

3 thoughts on “Swim timer code listing”

  1. Sealing the switches seems to be a failure point in the design, especially as they wear. How about using hall effect devices to replace the switches. Buttons on the outside of jar would contain a magnet which activates the switch when it is pushed and moved close to the hall effect device on the inside of the lid. Or, use reed switches and magnets.
    And, you could gut one of the rechargeable toothbrushes that use inductive coupling to charge a rechargeable battery inside the jar. Seal the toothbrush electronics in the jar and use the charger as is to charge the unit.
    Or if you want to go green, use solar charging to charge a rechargeable battery in the jar. During the day, the unit would charge by the pool.

    1. Properly designed o-ring seals are pretty reliable. I am planning to modify the design for magnetic reed switches. Hopefully they will be slightly simpler than the current switch mechanism. Removing the switch seals might improve reliability. Properly designed static o-ring seals are extremely reliable.

      As far as rechargeable, I don’t think that would be an improvement. It is not difficult to change the batteries twice a year and the big benefit is that I don’t need to give the timer special handling different from the rest of my swim gear. That means it won’t be left on the charger when I go to the pool or left somewhere to get sunlight.

  2. I just started swimming laps in my backyard pool just before bedtime. I can use the exercise and the workout helps me get to sleep when I’m done. I wanted to bring a clock outside to time my workout but I’d need one that would be weatherproof, and visible in the dark. So your post is timely. I have a surplus Arduino and quite a few loose 7 segment displays, but they are common cathode. Looking at your code I realized you are scanning the display bass ackwards. IE: turning on the same segment as required in all digits at once instead of all segments as required in each digit one at a time. This way you keep within the 20ma current limit of the AVR I/O (clever!). However if I reverse the logic levels for the segments and digits I can substitute common cathode displays for the common anode. Adafruit now has some nice waterproof ‘Otter boxes’ that might be the ticket for this. I’ll probably run the timer off a wall wart as there is an outlet available by the pool.

Leave a Reply

Your email address will not be published. Required fields are marked *