Clock selector

Hi All,

I know this is an extremely ambitious first project for a code newbie, but I'm kind of stuck having to do this for work. Not really by choice.

I have to replace the digital clock in an appliance with a larger one, and the time needs to be changed at the start of each scene to reflect the fact that time has passed since the end of the last scene. So here's how I'm trying to implement it:

A 16 position rotary selector switch
A 4 digit 7 segment LED display
a RTC module

The Arduino will read the position of the selector switch and send the corresponding signal to the display:
Position 0 = off
1 Time = 9:00
2 off
3 Time = 7:30
4 off
5 Time = 8:00
6 off
7 Time = 9:45
8 off
9 Time = 11:45

The Arduino will have to read the state of the switch. If it's at a position that requires a time to be displayed, the start time will be established and sent to the display, then the RTC takes over and bumps the digits as time passes.

I know I'm going to be using the Wire library, and there will be several if...else clauses. I've started going through the info on the RTC and the display in order to understand if I'll have to convert between decimal, bcd, 0x, etc. It's pretty daunting stuff if you've never seen it before.

Am I missing anything else obvious?

As I mentioned above, this is my first project ever, and my first experience dealing with the software side, too. So please be constructive in the criticism :slight_smile:

Well, I didn't quite understand what you are pretending.

It is possible to find working examples for the DS1307 RTC and a library for that too... so you got the time synchronization covered and all the conversions won't be done by you. Although the bcd and hex conversion is "funny". But I'm a geek!

It sounds like you are using this for a performance or something, which I would imagine means it wont be running for more than several hours at a time. What this means is that you don't need need an RTC. You can just use the internal clock and Millis() and this should be accurate enough for your purposes. Here is what i would imagine the code would look like (its pseudocode -- will not compile, i promise)

void loop(){
if switchreading != lastreading {
mode = switchreading
switch (mode) {
    case 1:
time = 900
 led.display (9:00)
      break;
    case 2:
time = 1100
led.display(11:00)
      break;
  case 3:
time = 100
led.display(1:00)
      break;
}}
  unsigned long currentMillis = millis();
 
  if(currentMillis - previousMillis > 60000) { //If one minute has passed
    // save the last time you updated the LED
    previousMillis = currentMillis;  
time++;
  led.display(time);
}
}

Obviously you will have to deal with hour-rollover but this should give you an idea of how it will work.

I'd go for a RTC... :\ It's a bit more reliable.

I'd go for a RTC... :\ It's a bit more reliable.

No, its extra complication and something more to go wrong / take up your time and effort. millis() is accurate enough for a theatre performance unless there's someone on the audience with an atomic clock!

However it might be a good idea if after the show the appliance is to return to normal use...

petergrimes:
...The Arduino will have to read the state of the switch. If it's at a position that requires a time to be displayed, the start time will be established and sent to the display, then the RTC takes over and bumps the digits as time passes.

As a post above suggests, you are overcomplicating things by using an RTC. You are relying on a human being to switch the rotary switch. Are they accurate to plus or minus a second?

Let us say that the clock on your appliance displays hours and minutes. What you want your code to do is to switch the display on in a certain configuration depending on the position of the rotary switch (switch - case is neater than a load of if statements for this), delay 60 seconds and then increment the display by one unit, checking for rollover.

You say your project is ambitious - I would disagree. If you break it down into small atomic or elemental steps, it becomes very simple.

Start with your rotary switch. If it is 16 position, it will probably require four input pins. Write a snippet of code first of all that reports to you if a single input pin is high or low. Now expand that to report correctly on the four possible states when two pins are used. Expand again to eight states/three pins and finally to sixteen states/four pins. Tie those sixteen states to a variable (so it will have a value of between 0 and 15). Test the value of that variable and make your output display react accordingly.

Thanks for the pointers!

I'm glad to hear that it's not only doable, but that the RTC isn't necessary. I was thinking it would simply sending the proper code to display the next number, not so much for accurate timing. But simpler is better, both in hardware and in software.

Yes, this is for a stage performance. In the past we've always done this the easy way: a couple of alarm clocks that a technician sets at the top of the show. It's easy to screw up and very clumsy. I've realized the need to learn more about electronics and control, and luckily I've had a couple of weeks to devote to this.

I'll post back as the project progresses (or stumbles). My goal is to have it working on Monday, since actors are onstage on Wednesday. Ugh.

OK, I'm kind of stuck.

I'm able to read the state of the switch (determine which pins are active) and output that to the serial monitor. And I've written up the different cases for what to do in each pin configuration.

I also have the display working properly, at least, it's counting up just fine. I can also start it at whatever arbitrary time I choose by changing a value in the sketch.

But I haven't been able to join the two parts. When I run the following code, the serial monitor returns values for the pin states. So I know that's fine. But the display doesn't ever light up. Also, if I run the Gravitech example sketch for the display, which simply counts from 0 to 9999 and then rolls over, the display will hang on whatever number is on the screen when I start to upload my hybrid sketch. I can run through all of the switch positions without a change in the display even though the serial monitor is returning changes in the pin states.

Here's where I think I'm failing:
-First of all I think the whole loop needs to be structured differently.
-Right now it's always checking the state of the input pins, so there's no way for it to get out of that loop. -I think the display should be counting all the time, which I don't think would happen here.
-I also can't figure out how to tell the display to STOP displaying outside of the cases that I've defined. Of course, for that matter I haven't figured out how to START it displaying either...

I'm not trying to reinvent the wheel, which is why I'm relying on pasting in code that already works. It seems that I'm missing something obvious structurally, but I'm simply out of my element here.

#include <Wire.h> 
int oldState(0); //sets a variable to store the previous switch state

void setup() {
  Serial.begin(9600);
  pinMode(1, INPUT);  //these are the pins that correspond to the pins on the switch
  pinMode(2, INPUT);
  pinMode(4, INPUT);
  pinMode(8, INPUT);
  Wire.begin();        // join i2c bus (address optional for master) 
  delay(500);
  }

void loop() {
  
/************** First we read the state of the switch's legs ************/

  
  int sensorValue1 = digitalRead(1); // read the first pin
  Serial.print("Pin 1: ");           
  Serial.println(sensorValue1, DEC);  // print the value (0 or 1) on the serial monitor
  int range1 = (sensorValue1, 0, 1);
  
  int sensorValue2 = digitalRead(2);
  Serial.print("Pin 2: ");
  Serial.println(sensorValue2, DEC);
  int range2 = (sensorValue2, 0, 1);
  
  int sensorValue4 = digitalRead(4);
  Serial.print("Pin 4: ");
  Serial.println(sensorValue4, DEC);
  int range4 = (sensorValue4, 0, 1);
  
  int sensorValue8 = digitalRead(8);
  Serial.print("Pin 8: ");
  Serial.println(sensorValue8, DEC);
  int range8 = (sensorValue8, 0, 1);
  
  Serial.println();  //print the state (eg 1011) to the serial monitor 

int switchState = (sensorValue1, sensorValue2, sensorValue4, sensorValue8);  //set a variable to record the current switch state
  
if (switchState != oldState) {  //compare the old value to the current one

  /*-----> code copied from Gravitech Example, confirmed to work in that sketch ----->*/  
  const int lookup[10] = {0x3F,0x06,0x5B,0x4F,0x66, 
                          0x6D,0x7D,0x07,0x7F,0x6F};
  int Count, Thousands, Hundreds, Tens, Base;
  
  Wire.beginTransmission(0x38); 
  Wire.send(0);
  Wire.send(B01000111);
  Wire.endTransmission();
  /*------> end of copy ------>*/
  
switch (switchState) {
    
    case 1100:    //if leg1 and leg2 are on, then start counting at 900, and then add 1 digit per second
       
       /*----> code copied from Gravitech, confirmed to work outside of switch cases ---->*/
       for (Count=900;Count<=9999;Count++)
      {
      Wire.beginTransmission(0x38);
      Wire.send(1);
      Thousands = Count/1000;
      Hundreds = (Count-(Thousands*1000))/100;
      Tens = (Count-((Thousands*1000)+(Hundreds*100)))/10;
      Base = Count-((Thousands*1000)+(Hundreds*100)+(Tens*10));
      Wire.send(lookup[Base]);
      Wire.send(lookup[Tens]);
      Wire.send(lookup[Hundreds]);
      Wire.send(lookup[Thousands]);
      Wire.endTransmission();
      delay(1000);
      } 
      /*------> end copy ----->*/
    break;
  
   case 1010:     //if legs 1 and 4 are on, then start counting at 730 and add 1 per second.
     for (Count=730;Count<=9999;Count++)
    {
     Wire.beginTransmission(0x38);
     Wire.send(1);
     Thousands = Count/1000;
     Hundreds = (Count-(Thousands*1000))/100;
     Tens = (Count-((Thousands*1000)+(Hundreds*100)))/10;
     Base = Count-((Thousands*1000)+(Hundreds*100)+(Tens*10));
     Wire.send(lookup[Base]);
     Wire.send(lookup[Tens]);
     Wire.send(lookup[Hundreds]);
     Wire.send(lookup[Thousands]);
     Wire.endTransmission();
     delay(1000);
    } 
    break;
  
   case 1110:   //if legs 1, 2, and 4 are on, start counting at 845 and add 1 per second
     for (Count=845;Count<=9999;Count++)
    {
     Wire.beginTransmission(0x38);
     Wire.send(1);
     Thousands = Count/1000;
     Hundreds = (Count-(Thousands*1000))/100;
     Tens = (Count-((Thousands*1000)+(Hundreds*100)))/10;
     Base = Count-((Thousands*1000)+(Hundreds*100)+(Tens*10));
     Wire.send(lookup[Base]);
     Wire.send(lookup[Tens]);
     Wire.send(lookup[Hundreds]);
     Wire.send(lookup[Thousands]);
     Wire.endTransmission();
     delay(1000);
    } 
    break;
  
  case 1001:  //legs 1 and 8 are on start counting at 930 and add 1 per second
    for (Count=930;Count<=9999;Count++)
    {
    Wire.beginTransmission(0x38);
    Wire.send(1);
    Thousands = Count/1000;
    Hundreds = (Count-(Thousands*1000))/100;
    Tens = (Count-((Thousands*1000)+(Hundreds*100)))/10;
    Base = Count-((Thousands*1000)+(Hundreds*100)+(Tens*10));
    Wire.send(lookup[Base]);
    Wire.send(lookup[Tens]);
    Wire.send(lookup[Hundreds]);
    Wire.send(lookup[Thousands]);
    Wire.endTransmission();
    delay(1000);
    } 
    break;
  
  case 1101:  //if legs 1, 2, and 8 are on start counting at 1130 and add 1 per second
    for (Count=1130;Count<=9999;Count++)
    {
    Wire.beginTransmission(0x38);
    Wire.send(1);
    Thousands = Count/1000;
    Hundreds = (Count-(Thousands*1000))/100;
    Tens = (Count-((Thousands*1000)+(Hundreds*100)))/10;
    Base = Count-((Thousands*1000)+(Hundreds*100)+(Tens*10));
    Wire.send(lookup[Base]);
    Wire.send(lookup[Tens]);
    Wire.send(lookup[Hundreds]);
    Wire.send(lookup[Thousands]);
    Wire.endTransmission();
    delay(1000);
    } 
    break;
 delay (50); //debounce the switch

  }
  oldState = switchState;  //store the current value of the switch for the next time through the loop
  }
 delay (2000); // wait 2 seconds before heading back the start to take a new switch reading
}

Your code is somewhat confusing, so I rewrote it as best I could (mostly concerned about how to transmit the data to the screen). It compiles, but I don't have your hardware to test with.

Some notes:
#1. Don't use pin #1 - it's needed for Serial, and setting it to input does bad things.
#2. Use mod (%) operators - it's the integer remainder function.

example: if i = 1234;
i / 1000 = 1
i % 1000 = 234
(i % 1000) / 100 = 2
i % 100 = 34
(i % 100) / 10 = 3
i % 10 = 4

#include <Wire.h> 

// pins for the switch
// Don't use pin 1 - it's for serial.
// they are backwards in order to fit the original code's cases.
uint8_t switch_pin[4] = {8, 4, 2, 1};

uint8_t p_switchPos;

unsigned long nextTime, timeCounter;

const int lookup[10] = {0x3F,0x06,0x5B,0x4F,0x66, 
                        0x6D,0x7D,0x07,0x7F,0x6F};

void setup() {
  // begin serial at 115.2kbaud
  Serial.begin(115200);
  
  // initialize the inputs
  for (uint8_t i = 0; i < 4; i++) {
   pinMode(switch_pin[i], INPUT);
  }  
  
  // initialize I2C
  Wire.begin();

  // reset the time counter
  timeCounter = 0;
  
  // reset timer
  nextTime = millis();
  
  // reset the switch position
  p_switchPos = 16; // set it to something impossible to get
  
  // not sure what this does, but it's in the original.
  Wire.beginTransmission(0x38); 
  Wire.send(0);
  Wire.send(B01000111);
  Wire.endTransmission();
}

void loop() {
  // read the switch
  uint8_t switchPos; // switchpos will have a value between 0 and 15.
  for (uint8_t i = 0; i < 4; i++) {
    switchPos |= digitalRead(switch_pin[i]) << i;
  }
  
  // if switch position has changed
  if (switchPos != p_switchPos) {
    switch(switchPos) {
    // case 1001 from original code
    case 9:
      // set starting time to 930
      timeCounter = 930;
      break;
    // case 1010, from original code
    case 10:
      // set starting time to 730
      timeCounter = 730;
      break;
    // case 1100, from original code
    case 12:
      // set starting time to 900
      timeCounter = 900;
      break;
    // case 1101 from original code
    case 13:
      // set starting time to 1130
      timeCounter = 1130;
      break;
    // case 1110 from original code
    case 14:
      // set starting time to 845
      timeCounter = 845;
      break;
    // if it's not any of the above cases
    default:
      // set starting time to 0
      timeCounter = 0;
      break;
    }
  
    // set previous pos to current pos
    p_switchPos = switchPos;
  }
  
  unsigned long currentTime = millis();
  // if 1000 milliseconds have passed
  if (currentTime >= nextTime) {
    // increment time
    timeCounter++; // add one second to the time
    nextTime = currentTime + 1000; // wait another 1000ms
  
    // display current time
    int thousands, hundreds, tens, base;
  
    // split into digits
    thousands = timeCounter / 1000;
    hundreds = (timeCounter % 1000) / 100;
    tens = (timeCounter % 100) / 10;
    base = (timeCounter % 10);

    Wire.beginTransmission(0x38);
    Wire.send(1);
    Wire.send(lookup[base]);
    Wire.send(lookup[tens]);
    Wire.send(lookup[hundreds]);
    Wire.send(lookup[thousands]);
    Wire.endTransmission();
  }
}

Please tell me if the code works - I just mocked it up in the last five minutes or so, and it may be buggy.

Quick reply in gratitude for a quick response!

This code compiled at just over half the byte-size of mine (show-off!), and it works much better :slight_smile:

The display starts counting from the proper number at each of the switch positions I needed. There are a couple things it doesn't do, (for instance the clock starts ticking as soon as the power is on, and continues in between switch positions whereas I need it to be Off on the even positions and On in the odd ones). I'm going to try to work that in.

But first, of course, I'm going to have to understand how you did what you did. I'm still so new to this I just don't get a lot of it.

For what it's worth I did improve my sketch after my last post. I mapped some different integer values to the input states, such as pin4 (0,1,0,4) so that different switch positions yielded different integer values. In this way I could assign cases better. I only discovered this flaw after asking the serial monitor to print the value of my switchState variable. The only value being returned under all positions was 0. I fixed that part, and was very pleased with myself!

Thanks again for taking a look at this.

How does this line work?

uint8_t switchPos; // switchpos will have a value between 0 and 15.
for (uint8_t i = 0; i < 4; i++) {
switchPos |= digitalRead(switch_pin*) << i;*

  • }*
    [/quote]
    I mean, how is does the program combine the values of the pins into a value between 0 and 15? In my code I explicitly assigned a range through the mapping, but I don't see that here.

To make it only run on odd (not even) variables, add in a new boolean and put the clock 'increment' section in an if statement

bool isRunning;

// ... 

isRunning = switchPos % 2 == 1; // isRunning is true when switchPos is odd

// ...

if (isRunning) {
  timeCounter++;
}

In this code snippet:

  uint8_t switchPos; // switchpos will have a value between 0 and 15.
  for (uint8_t i = 0; i < 4; i++) {
    switchPos |= digitalRead(switch_pin[i]) << i;
  }

I take advantage of the fact that digitalRead returns a boolean value, that is, a value that is either 0 or 1. It's actually a really condensed version, which is probably easier to read like this:

  uint8_t switchPos; // switchpos will have a value between 0 and 15.
  bool bit0, bit1, bit2, bit3;

  bit0 = digitalRead(switch_pin[0]);
  bit1 = digitalRead(switch_pin[1]);
  bit2 = digitalRead(switch_pin[2]);
  bit3 = digitalRead(switch_pin[3]);

  switchPos = (bit3 << 3) | (bit2 << 2) | (bit1 << 1) | (bit0 << 0);

Breaking it down part by part:

  bit0 = digitalRead(switch_pin[0]);
  bit1 = digitalRead(switch_pin[1]);
  bit2 = digitalRead(switch_pin[2]);
  bit3 = digitalRead(switch_pin[3]);

This reads each switch pin (switch_pin[] is an array holding the four pins) and stores them as boolean values

  switchPos = (bit3 << 3) | (bit2 << 2) | (bit1 << 1) | (bit0 << 0);

The << operator is a left-bit-shift, and works as such:

x << y = x * 2y

so if bit3 = 1, then bit3 << 3 is 23 = 8.

This is not very convenient in decimal, so lets look at it in binary.

bit3 is true, so we can see that in binary, you have something like 0b00000001. (the 0b denotes base 2)
If we multiply that by 8, or shift it three positions left, we get this: 0b00001000

Lets take the case where bit0 is 0, bit1 is 1, bit2 is 0, and bit3 is 1
In the code, we shift each one by a certain number of bits:
bit3 << 3 means it's shifted 3 bits, to 0b00001000
bit2 << 2 means it's shifted 2 bits, to 0b00000000 (since it's zero we don't see anything)
bit1 << 1 means it's shifted 1 bit, to 0b00000010
bit0 << 0 means it's not shifted, but it's zero, so it's 0b00000000

The other operator you probably don't know is the | operator, which is a bitwise or. It works such that if a given bit is true in EITHER operand, that bit will be true in the final operand, kind of like addition.

So you get something like this:
after shifting:
bit3 0b00001000
bit2 0b00000000
bit1 0b00000010
bit0 0b00000000
---------------------- After ORing them all together we get this:
0b00001010
If you convert that back in to base 10, you'll have a value of (0 * 1 + 1 * 2 + 0 * 4 + 1 * 8) = 10.

Notice that the maximum value you can get from this is if bit0, bit1, bit2, and bit3 are true, which would end up as 0b00001111
Converting that to decimal makes (1 * 1 + 1 * 2 + 1 * 4 + 1 * 8) = 15.
The minimum value is therefore if bit0, bit1, bit2, and bit3 are all false, which ends up as the expected 0b00000000
Converting that to decimal is just going to give us 0.

Thus, the code snippet takes in four binary values and makes it into a four-bit number, and when using decimal values, the limits of a four-bit number are 0 to 15. (--edit-- had 16 here originally, that was a fail --)

Hope that made sense - bitwise operations aren't the easiest to understand, but they make code a lot cleaner when you're dealing with boolean values =).

That was a very clear explanation. I understand, now, how your commands assigned a numerical value for the switchPosition.

Thanks again for taking the time to explain some of this.

I take advantage of the fact that digitalRead returns a boolean value, that is, a value that is either 0 or 1.

I had considered trying to use a boolean form, but the reference section on Boolean lead me to believe that it returned the word 'false' or 'true'. I didn't know that 0 and 1 were interchangeable with the words. It makes sense now, of course.

This is very useful stuff - I can easily imagine referring back to it in the future :slight_smile:

HIGH and TRUE are not strings but constants that represent 1.
In the same manner, LOW and FALSE represent a 0. I know that 1 and high and 0 and low are always interchangeable, and I'm pretty sure the true and false fit in their too.
Just make sure they are in CAPS.

Best of luck with the rest of your project!

If you want to be technically correct, it should have been !(!(digitalRead(pin)), since that guarantees a 0 or 1 value. In C and C++, TRUE is a constant defined as one, but is only required to be not-zero - that is, -1 or 1000 would also evaluate as true in a boolean sense. Thus, a pair of inversions ought to be used to normalize the output of a boolean function to 0 or 1:

if the function returns 1000 (true)
the first ! converts it to 0 (false)
the second ! converts it back to 1 (true)

However, since in Arduino TRUE (and HIGH, and a number of other constants) all evaluate to 1, since they're defined with #define HIGH (1), you can skip the logical inversions and just know that it works. =).

UPDATE:

I successfully incorporated the odd / even condition to determine if the counter increments or not, depending on the modulus of the switchPosition. In order to do that, I had to jigger around some of my wiring arrangements and pin assignments, as the code below shows.

Due to the physical layout of the switch and the bridges between the legs inside, I had to reassign the INPUT pins. I also had to switch the order that the swtichPos value was constructed. But I'm pleased that I got it right on the first try.

Now there are only two things left to do before my project is complete:

  1. Figure out how to send a wire.h command that turns the display off in the default case. I've tried sending various 0's and such, but to no effect.
  2. Solder it all up onto a project board and install back in the appliance. If I can't do a command that shuts off the display, I'll have to make a physical shutter that passes between the appliance window and the display. I really don't want to have to do that, but at least it's a back up solution.

Code:

#include <Wire.h>

// pins for the switch
// Don't use pin 1 - it's for serial.
// the pins are read in an order such that the switch_pin will have an odd value for my action cases
uint8_t switch_pin[4] = {2, 3, 4, 5};

uint8_t p_switchPos;

bool isRunning;

unsigned long nextTime, timeCounter;

const int lookup[10] = {0x3F,0x06,0x5B,0x4F,0x66,
0x6D,0x7D,0x07,0x7F,0x6F};

void setup() {
// begin serial at 115.2kbaud
Serial.begin(9600);

// initialize the inputs
/for (uint8_t i = 0; i < 4; i++) {
pinMode(switch_pin
, INPUT);[/color]*

  • } *
    _ */_
  • for (uint8_t i = 2; i < 6; i++) {*
    pinMode(switch_pin*, INPUT);
    _
    }_
    _
    // initialize I2C*_
    * Wire.begin();*
    * // reset the time counter*
    * timeCounter = 0;*

* // reset timer*
* nextTime = millis();*

* // reset the switch position*
* p_switchPos = 16; // set it to something impossible to get*

* // not sure what this does, but it's in the original.*
* Wire.beginTransmission(0x38);*
* Wire.send(0);*
* Wire.send(B01000111);*
* Wire.endTransmission();*
}
void loop() {
* // read the switch*
* uint8_t switchPos; // switchpos will have a value between 0 and 15.
for (uint8_t i = 0; i < 4; i++) {
switchPos |= digitalRead(switch_pin) << i;
_ }*_

* //check to see if the binary value of switchPos is odd. *
* isRunning = switchPos % 2 == 1;*

* // if switch position has changed*
* if (switchPos != p_switchPos) {
_ switch(switchPos) {
// case 1100 from original code*
* case 3:
// set starting time to 900*
* timeCounter = 900;
break;
// case 1010, from original code*
* case 5:
// set starting time to 730*
* timeCounter = 730;
break;
// case 1110, from original code*
* case 7:
// set starting time to 845*
* timeCounter = 845;
break;
// case 1001 from original code*
* case 9:
// set starting time to 930*
* timeCounter = 930;
break;
// case 1101 from original code*
* case 11:
// set starting time to 845*
* timeCounter = 1130;
break;
// if it's not any of the above cases*
* default:
// set starting time to 0*
* timeCounter = 0;
break;
}*_

* // set previous pos to current pos*
* p_switchPos = switchPos;
_ }*_

* unsigned long currentTime = millis();*
* // if 1000 milliseconds have passed*

* //if the binary value of switchPos is odd, then time may increment*
* if (isRunning) {*

* if (currentTime >= nextTime) {*
* // increment time*
* timeCounter++; // add one second to the time*
* nextTime = currentTime + 1000; // wait another 1000ms*

* // display current time*
* int thousands, hundreds, tens, base;*

* // split into digits*
* thousands = timeCounter / 1000;*
* hundreds = (timeCounter % 1000) / 100;*
* tens = (timeCounter % 100) / 10;*
* base = (timeCounter % 10);*
* Wire.beginTransmission(0x38);*
* Wire.send(1);*
* Wire.send(lookup[base]);*
* Wire.send(lookup[tens]);*
* Wire.send(lookup[hundreds]);*
* Wire.send(lookup[thousands]);*
* Wire.endTransmission();*
* }*
* }*
}
[/quote]

I commented in your other post about turning off the LEDs, it should work that way.

You have forgotten to implement minute/hour rollover. For example after 959, it will go to 960 and 961.

Thanks, Bilbo. I replied in the other thread :stuck_out_tongue:

I'm aware of the hour rollover issue. Luckily for me the scenes of the play are short enough that it shouldn't be an issue. And if it becomes an issue, I will work on it at that point.

For now, my priorities have to be soldering up the project and then installing it. I also forgot that I need to put in some status LEDs so the operator can see what state the display is in. Then get over to the warehouse by 4 to load a truck, and so on.

I used the suggestion from the other thread to send
wire.send(0)
to all 4 digits and it worked like a charm.

In preparation for soldering up the project I wanted to make sure the clock will work when it's slowed down to increment once per minute, rather than once per second as coded:

nextTime = currentTime + 1000; // wait another 1000ms

I changed that to + 60000, thinking that would be the only thing necessary. What I thought would be a simple change has consequences I wasn't expecting.

When the switch position is changed, it takes a minute for the display to come on. I'm not sure why. I thought the display would still read the value of the switch position, check the cases, and display accordingly. But it doesn't.

What am I missing?

relevant segment of code repasted from complete code in earlier post:

unsigned long currentTime = millis();
// if 1000 milliseconds have passed

//if the binary value of switchPos is odd, then time may increment
if (isRunning) {

if (currentTime >= nextTime) {
// increment time
timeCounter++; // add one second to the time
nextTime = currentTime + 60000; // wait another 1 minute

// display current time
int thousands, hundreds, tens, base;

// split into digits
thousands = timeCounter / 1000;
hundreds = (timeCounter % 1000) / 100;
tens = (timeCounter % 100) / 10;
base = (timeCounter % 10);

Wire.beginTransmission(0x38);
Wire.send(1);
Wire.send(lookup[base]);
Wire.send(lookup[tens]);
Wire.send(lookup[hundreds]);
Wire.send(lookup[thousands]);
Wire.endTransmission();

}
}

The display is only refreshed on the next time the number changes - that is, one minute after you set it.

To fix, move this section:

  // display current time
    int thousands, hundreds, tens, base;
  
    // split into digits
    thousands = timeCounter / 1000;
    hundreds = (timeCounter % 1000) / 100;
    tens = (timeCounter % 100) / 10;
    base = (timeCounter % 10);

    Wire.beginTransmission(0x38);
    Wire.send(1);
    Wire.send(lookup[base]);
    Wire.send(lookup[tens]);
    Wire.send(lookup[hundreds]);
    Wire.send(lookup[thousands]);
    Wire.endTransmission();

out of the

if (currentTime >= nextTime) {
// ...
}

block to the

if (isRunning) {
// ...
}

block.

Essentially what you're dealing with is an off-by-one error: you need to wait an increment before the display will change, so the display seems to lag for a minute. By moving it to the isRunning block, you refresh the display continuously, thereby avoiding the problem.