Adding a countdown timer to a two click-counters

Ok, so I gotta apologize right off the hop for the very confusing title. Let me first try to explain the project, then I'll get to my question.

The project is for a hot-wire cutter for styrofoam, not your typical table-top hot-wire cutter that most of you would be familiar with, but a much longer hot-wire that requires two people to use.

I work in cinema in the art department, and many of our projects use giant blocks of styrofoam that are 4' x 4' x 20' long. So if we are doing cuts over the length, my partner is at the other end, often in a loud environment and we might both be crouched behind each end of the styrofoam block. Complicated cuts require marking check-points at the corners of each cut, and yelling out numbers in said loud environment, crouched behind a massive block of styrofoam wasn't working for me. So I decided to make a hot-wire with integrated counters (TM1637 Display) and a pushbutton to increment the count on each handle, so far - so good. Both myself and my partner can increment the count as we reach each checkpoint and we both know where we're at without yelling.

With these long cuts, the wire in the centre of the block tends to be colder than the wire ate the ends. Our technique is for each of us to reach our checkpoint and hold at that point for x seconds while the wire in the middle catches up, then continue the cut. Again this requires communication.

Now I get to my question. I would like to add an adjustable countdown timer to my existing code, once that would detect that my number and my partner's number matched, would display the countdown, then when it reached zero would flash once then return to our incremental number display. This countdown timer should be adjustable, as longer pieces require a longer delay, shorter ones not so much. Say adjustable between 10 seconds, all the way down to 0 - zero however should negate the countdown timer sequence altogether and just show our increments.

I am a complete newbie with this, though I have managed to get the counter code working through lots of cut'n'paste programming. I am reluctant to start fiddling with the modification i am seeking to do though as I really don't want to break the code I have right now and I really have no idea where to start. Any help is greatly appreciated.

The following is the working code that I have so far:

/*
 * This is Arduino "Hot-Wire foam cutter checkpoint counter" using a pair of TM1637 seven-segment LED Displays.
 * Long pieces of syrofoam require two people to cut the length. This sketch allows for each user to increment the counter
 * as they arrive at each check-point marked on the piece, as well as have a visual read-out showing their location as well as their partner's.
 * 
 * The original code is based on "Robojax Touch Counter V3 using TM1637 4 digits LED display"
 * Link: https://robojax.com/learn/arduino/?vid=robojax_touch_counter_V3_TM1637
 * 
 */

//***** beginning of TM1637 Display
#include <Arduino.h>
#include <TM1637Display.h>

// Master Display connection pins (Digital Pins)
#define CLK1 6 // D6
#define DIO1 7 // D7

// Slave Display connection pins (Digital Pins)
#define CLK2 8 // D8
#define DIO2 9 // D9

// The amount of time (in milliseconds) between readings
#define TEST_DELAY   200

TM1637Display display1(CLK1, DIO1);  // define display 1 (Master) object
TM1637Display display2(CLK2, DIO2);  // define display 2 (Slave) object

uint8_t blank[] = { 0x0, 0x0, 0x0, 0x0 };// data to clear the screen

//***** end of TM1637 Display

const int masterPin = 10;  // D10: input pin where Master Increment button is connected
const int slavePin = 11;  // D11: input pin where Slave Increment button is connected
const int resetPin = 12;  // D12: the input pin for reset button
const int touchDelay = 500;  //millisecond delay between each touch

int MasterCount=0;  // variable holding the count number
int SlaveCount=0;  // Same, for slave.

void setup() {
    
  Serial.begin(9600);  // initialize serial monitor with 9600 baud
  Serial.println("Hotwire Checkpoint Counter");  
  pinMode(masterPin,INPUT_PULLUP);  // define a pin for master display module
  pinMode(slavePin,INPUT_PULLUP);  // define a pin for slave display module
  pinMode(resetPin,INPUT_PULLUP);  // define a pin for reset button

  display1.setBrightness(0x0f);  // set brightness of display 1
  display2.setBrightness(0x0f);  // set brightness of display 2
   
  uint8_t data8888[] = { 0xff, 0xff, 0xff, 0xff };  // show all segments.
  display1.setSegments(data8888); // display 8888 on display1 for test.
  display2.setSegments(data8888); // display 8888 on display2 for test.
  delay(3000);  // 3 second 8888 display at start-up.
//  display1.setSegments(blank); // clear the Master screen from previous values.
//  display2.setSegments(blank); // clear the Slave screen from previous values.

  // Display "00:00" on both displays
  display1.showNumberDecEx(MasterCount, 0b01000000, true, 2, 0); // display Master count and colon on first part of Master display.
  display1.showNumberDec(SlaveCount, true, 2,2);  // display Slave count  on second part of Master display.
  display2.showNumberDecEx(SlaveCount, 0b01000000, true, 2,0);  // display Slave count and colon on first part of Slave display.
  display2.showNumberDec(MasterCount, true, 2,2);  // display Master count on second part of Slave display.
}



void loop() {


  int masterValue = digitalRead(masterPin);  // read masterPin and store it in masterValue
  int slaveValue = digitalRead(slavePin);  // read slavePin and store it in slaveValue

  // if masterValue is LOW
  if(masterValue == LOW)
  {
      MasterCount++;  // increment the Master count   
      
//      display1.setSegments(blank);  // clear Master screen from previous values.
//      display2.setSegments(blank);  // clear Slave screen from previous values.
      display1.showNumberDecEx(MasterCount, 0b01000000, true, 2, 0); // display Master count and colon on first part of Master display.
      display1.showNumberDec(SlaveCount, true, 2,2);  // display Slave count  on second part of Master display.
      display2.showNumberDecEx(SlaveCount, 0b01000000, true, 2,0);  // display Slave count and colon on first part of Slave display.
      display2.showNumberDec(MasterCount, true, 2,2);  // display Master count on second part of Slave display.
      
      Serial.print("Master: ");  // print the information
      Serial.print(MasterCount);  // print Master count
      Serial.print(" | Slave: ");
      Serial.print(SlaveCount);  // print Slave count
      Serial.println(".");        
      delay(touchDelay);  // touch delay time
  }

  // if slaveValue is LOW
  if(slaveValue == LOW)
  {
      SlaveCount++;  // increment the Slave count   
      
//      display1.setSegments(blank);  // clear Master screen from previous values.
//      display2.setSegments(blank);  // clear Slave screen from previous values.
      display1.showNumberDecEx(MasterCount, 0b01000000, true, 2, 0); // display Master count and colon on first part of Master display.
      display1.showNumberDec(SlaveCount, true, 2,2);  // display Slave count  on second part of Master display.
      display2.showNumberDecEx(SlaveCount, 0b01000000, true, 2,0);  // display Slave count and colon on first part of Slave display.
      display2.showNumberDec(MasterCount, true, 2,2);  // display Master count on second part of Slave display.

      Serial.print("Master: ");  // print the information
      Serial.print(MasterCount);  // print count
      Serial.print(" | ");
      Serial.print("Slave: ");  // print the information
      Serial.print(SlaveCount);  // print count
      Serial.println(".");     
    delay(touchDelay);  // touch delay time
  }
      
   // if reset switch is pushed
   if(digitalRead(resetPin) == LOW)
   {
   
      MasterCount=0;// reset master counter;
      SlaveCount=0;  // reset slave counter;
      Serial.println("Counter Reset.");//print the information
//      display1.setSegments(blank); // clear the screen from previous values   
//      display2.setSegments(blank); // clear the screen from previous values  
      display1.showNumberDecEx(MasterCount, 0b01000000, true, 2, 0); // display Master count and colon on first part of Master display.
      display1.showNumberDec(SlaveCount, true, 2,2);  // display Slave count  on second part of Master display.
      display2.showNumberDecEx(SlaveCount, 0b01000000, true, 2,0);  // display Slave count and colon on first part of Slave display.
      display2.showNumberDec(MasterCount, true, 2,2);  // display Master count on second part of Slave display.
      delay(touchDelay);  // touch delay time
   }



}

Always show us a good schematic of your proposed circuit.
Show us a good image of your ‘actual’ wiring.
Give links to components.

And

Tell us what does not work.

Hello Larry;

I didn't include a schematic as my question was strictly programming related, but here you go.

There are two independent circuits. The above circuit is the Hot-Wire circuit, running on variable A/C voltage supplied by a variac.

The lower circuit is the counter and display circuit. Each PCB and Counter set-up are housed in separate handles 25 feet apart.

So far, this all works as is. But like I said, I am looking to modify the program for an adjustable countdown action. I would in turn need to add three more momentary buttons to the MC, "increment", "decrement" and "reset". Those are not yet pictured.

Lets see if this still works the same as your original sketch.


Can you follow what is done in this sketch ?

We can talk about adding other components.

/*
   This is Arduino "Hot-Wire foam cutter checkpoint counter" using a pair of TM1637 seven-segment LED Displays.
   Long pieces of syrofoam require two people to cut the length. This sketch allows for each user to increment the counter
   as they arrive at each check-point marked on the piece, as well as have a visual read-out showing their location as well as their partner's.

   The original code is based on "Robojax Touch Counter V3 using TM1637 4 digits LED display"
   Link: https://robojax.com/learn/arduino/?vid=robojax_touch_counter_V3_TM1637

*/

#include <Arduino.h>
#include <TM1637Display.h>

//*********************************************************************************
//Master Display connection pins (Digital Pins)
#define CLK1               6
#define DIO1               7

//Slave Display connection pins (Digital Pins)
#define CLK2               8
#define DIO2               9

#define PUSHED             LOW
#define RELEASED           HIGH


//The amount of time (in milliseconds) between readings
#define TEST_DELAY         200

TM1637Display display1(CLK1, DIO1);      //define display 1 (Master) object
TM1637Display display2(CLK2, DIO2);      //define display 2 (Slave) object

uint8_t blank[] = { 0x0, 0x0, 0x0, 0x0 };// data to clear the screen

//*********************************************************************************

const byte heartbeatLED  = 13;
const byte resetPin      = 12;  //D12: the input pin for reset button
const byte slavePin      = 11;  //D11: input pin where Slave Increment button is connected
const byte masterPin     = 10;  //D10: input pin where Master Increment button is connected

//6, 7, 8, and 9 are used for the displays

const byte increment     = 5;
const byte decrement     = 4;
const byte MC            = 3;

byte lastMasterState;
byte lastSlaveState;
byte lastResetState;

const int touchDelay     = 500; //millisecond delay between each touch

int MasterCount          = 0;   //variable holding the count number
int SlaveCount           = 0;   //Same, for slave.

//timing stuff
unsigned long heartbeatMillis;
unsigned long switchMillis;


//*********************************************************************************
void setup()
{
  Serial.begin(9600);
  Serial.println("Hotwire Checkpoint Counter");

  pinMode(heartbeatLED, OUTPUT);

  pinMode(masterPin, INPUT_PULLUP);
  pinMode(slavePin, INPUT_PULLUP);
  pinMode(resetPin, INPUT_PULLUP);

  display1.setBrightness(0x0f);
  display2.setBrightness(0x0f);

  uint8_t data8888[] = { 0xff, 0xff, 0xff, 0xff };  //show all segments.
  display1.setSegments(data8888); //display 8888 on display1 for test.
  display2.setSegments(data8888); //display 8888 on display2 for test.
  delay(3000);  //3 second 8888 display at start-up.

  // Display "00:00" on both displays
  //display Master count and colon on first part of Master display.
  display1.showNumberDecEx(MasterCount, 0b01000000, true, 2, 0);

  //display Slave count  on second part of Master display.
  display1.showNumberDec(SlaveCount, true, 2, 2);

  //display Slave count and colon on first part of Slave display.
  display2.showNumberDecEx(SlaveCount, 0b01000000, true, 2, 0);

  //display Master count on second part of Slave display.
  display2.showNumberDec(MasterCount, true, 2, 2);

} //END of   setup()


//*********************************************************************************
void loop()
{
  //***************************************
  //time to toggle the heartbeat LED ?
  if (millis() - heartbeatMillis >= 500)
  {
    //restart the TIMER
    heartbeatMillis = millis();

    //toggle LED
    digitalWrite(heartbeatLED, !digitalRead(heartbeatLED));
  }

  //***************************************
  //time to check the switches ?
  if (millis() - switchMillis >= 100)
  {
    //restart the TIMER
    switchMillis = millis();

    checkSwitches();
  }

  //***************************************
  //other none blocking code goes here
  //***************************************

} //END of   loop()


//*******************************************************************************
void checkSwitches()
{
  byte currentState;

  //***********************************************         m a s t e r  P i n
  //masterPin switch
  currentState = digitalRead(masterPin);

  if (lastMasterState != currentState)
  {
    lastMasterState = currentState;

    if (currentState == PUSHED)
    {
    MasterCount++;  // increment the Master count

    //display Master count and colon on first part of Master display.
    display1.showNumberDecEx(MasterCount, 0b01000000, true, 2, 0);

    //display Slave count  on second part of Master display.
    display1.showNumberDec(SlaveCount, true, 2, 2);

    //display Slave count and colon on first part of Slave display.
    display2.showNumberDecEx(SlaveCount, 0b01000000, true, 2, 0);

    //display Master count on second part of Slave display.
    display2.showNumberDec(MasterCount, true, 2, 2);

    Serial.print("Master: ");
    Serial.print(MasterCount);
    Serial.print(" | Slave: ");
    Serial.print(SlaveCount);
    Serial.println(".");

    //delay(touchDelay);  //touch delay time
    }

  } //END of this switch

  //***********************************************         s l a v e P i n
  //slavePin switch
  currentState = digitalRead(slavePin);

  if (lastSlaveState != currentState)
  {
    lastSlaveState = currentState;

    if (currentState == PUSHED)
    {
    SlaveCount++;

    //display Master count and colon on first part of Master display.
    display1.showNumberDecEx(MasterCount, 0b01000000, true, 2, 0);

    //display Slave count  on second part of Master display.
    display1.showNumberDec(SlaveCount, true, 2, 2);

    //display Slave count and colon on first part of Slave display.
    display2.showNumberDecEx(SlaveCount, 0b01000000, true, 2, 0);

    //display Master count on second part of Slave display.
    display2.showNumberDec(MasterCount, true, 2, 2);

    Serial.print("Master: ");
    Serial.print(MasterCount);
    Serial.print(" | ");
    Serial.print("Slave: ");
    Serial.print(SlaveCount);
    Serial.println(".");

    //delay(touchDelay);  //touch delay time
    }

  } //END of this switch

  //***********************************************         r e s e t P i n
  //resetPin switch
  currentState = digitalRead(resetPin);

  if (lastResetState != currentState)
  {
    lastResetState = currentState;

    if (currentState == PUSHED)
    {
    MasterCount = 0;
    SlaveCount = 0;
    Serial.println("Counter Reset.");

    display1.showNumberDecEx(MasterCount, 0b01000000, true, 2, 0);
    display1.showNumberDec(SlaveCount, true, 2, 2);
    display2.showNumberDecEx(SlaveCount, 0b01000000, true, 2, 0);
    display2.showNumberDec(MasterCount, true, 2, 2);

    //delay(touchDelay);  //touch delay time
    }

  } //END of this switch

} //END of   checkSwithes()

25 feet is quite a distance :thinking:

A sequence of events list would be helpful for us old timers to fully understand what needs to be added :wink:

example

  • master toggles their button till they get to 10
  • slave toggles their button till they get to 10
  • . . .

EDIT

Explain a bit more ?

Don't you have a reset already ?

Same countdown time for each step or unique times for each step?

This sketch should be close:

EDIT
Added comments

//
//https://forum.arduino.cc/t/adding-a-countdown-timer-to-a-two-click-counters/1050342
//

//********************************************^************************************************
//  Version   YY/MM/DD     Comments
//  =======   ========     ========================================================
//  1.00      22/11/06     Running code
//
//
//
//********************************************^************************************************


/*
   This is Arduino "Hot-Wire foam cutter checkpoint counter" using a pair of TM1637 seven-segment LED Displays.
   Long pieces of Styrofoam require two people to cut the length. This sketch allows for each user to increment the counter
   as they arrive at each check-point marked on the piece, as well as have a visual read-out showing their location as well as their partner's.

   The original code is based on "Robojax Touch Counter V3 using TM1637 4 digits LED display"
   Link: https://robojax.com/learn/arduino/?vid=robojax_touch_counter_V3_TM1637
*/

#include <Arduino.h>
#include <TM1637Display.h>

//*********************************************************************************
//Master Display connection pins (Digital Pins)
#define CLK1               6
#define DIO1               7

//Slave Display connection pins (Digital Pins)
#define CLK2               8
#define DIO2               9

TM1637Display display1(CLK1, DIO1);      //define display 1 (Master) object
TM1637Display display2(CLK2, DIO2);      //define display 2 (Slave) object

//     01
//  20    02
//     40
//  10    04
//     08

uint8_t blank[]    = { 0x0, 0x0, 0x0, 0x0 };      //data to clear the screen
uint8_t data8888[] = { 0xff, 0xff, 0xff, 0xff };  //show all segments.
uint8_t donE[]     = { 0x5e, 0x5c, 0x54, 0x79 };  //donE

//*********************************************************************************

#define PUSHED             LOW
#define RELEASED           HIGH

#define ENABLED            true
#define DISABLED           false

const byte heartbeatLED  = 13;
const byte resetPin      = 12;
const byte slavePin      = 11;
const byte masterPin     = 10;

//9, 8, 7, and 6 are used for the displays

const byte increment     = 5;
const byte decrement     = 4;

bool setCountTimerFlag   = DISABLED;
bool adjustingTimerFlag  = DISABLED;
bool newPositionFlag     = DISABLED;

const byte maximumCount  = 10;

byte lastMasterState;
byte lastSlaveState;
byte lastResetState;
byte lastIncrementState;
byte lastDecrementState;

int setCount;
int counter;
int MasterCount;
int SlaveCount;

//timing stuff
unsigned long heartbeatMillis;
unsigned long switchMillis;
unsigned long setCountMillis;
unsigned long adjustingMillis;

//*********************************************************************************
void setup()
{
  Serial.begin(9600);
  Serial.println("Hotwire Checkpoint Counter");

  pinMode(heartbeatLED, OUTPUT);

  pinMode(masterPin, INPUT_PULLUP);
  pinMode(slavePin,  INPUT_PULLUP);
  pinMode(resetPin,  INPUT_PULLUP);
  pinMode(increment, INPUT_PULLUP);
  pinMode(decrement, INPUT_PULLUP);

  display1.setBrightness(0x0f);
  display2.setBrightness(0x0f);

  display1.setSegments(data8888); //display 8888 on display1 for test.
  display2.setSegments(data8888); //display 8888 on display2 for test.
  delay(3000);                    //3 second 8888 display at start-up.

  // Display "00:00" on both displays
  //display Master count and colon on first part of Master display.
  display1.showNumberDecEx(MasterCount, 0b01000000, true, 2, 0);

  //display Slave count  on second part of Master display.
  display1.showNumberDec(SlaveCount, true, 2, 2);

  //display Slave count and colon on first part of Slave display.
  display2.showNumberDecEx(SlaveCount, 0b01000000, true, 2, 0);

  //display Master count on second part of Slave display.
  display2.showNumberDec(MasterCount, true, 2, 2);

} //END of   setup()


//*********************************************************************************
void loop()
{
  //***********************************************         heartbeat T I M E R
  //time to toggle the heartbeat LED ?
  if (millis() - heartbeatMillis >= 500)
  {
    //restart the TIMER
    heartbeatMillis = millis();

    //toggle LED
    digitalWrite(heartbeatLED, !digitalRead(heartbeatLED));
  }

  //***********************************************         check switches T I M E R
  //time to check the switches ?
  if (millis() - switchMillis >= 100)
  {
    //restart the TIMER
    switchMillis = millis();

    checkSwitches();
  }

  //***********************************************         setCount T I M E R
  //if enabled is it time to once again display Master and Slave values ?
  if (adjustingTimerFlag == ENABLED && millis() - adjustingMillis >= 2000ul)
  {
    //stop this TIMER
    adjustingTimerFlag = false;

    //re-display the Master and Slave values
    //display Master count and colon on first part of Master display.
    display1.showNumberDecEx(MasterCount, 0b01000000, true, 2, 0);

    //display Slave count  on second part of Master display.
    display1.showNumberDec(SlaveCount, true, 2, 2);

    //display Slave count and colon on first part of Slave display.
    display2.showNumberDecEx(SlaveCount, 0b01000000, true, 2, 0);

    //display Master count on second part of Slave display.
    display2.showNumberDec(MasterCount, true, 2, 2);

  }

  //***********************************************         counting down T I M E R
  //are we counting down ?
  if (setCountTimerFlag == ENABLED)
  {
    //has 1 second gone by ?
    if (millis() - setCountMillis >= 1000ul)
    {
      //restart this TIMER
      setCountMillis = millis();

      //update the displays
      //display setCount Master display.
      display1.setSegments(blank); //clear the Master screen from previous values.
      display1.showNumberDecEx(counter, 0b00000000, false, 4, 0);


      //display setCount Slave display.
      display2.setSegments(blank); //clear the Slave screen from previous values.
      display2.showNumberDecEx(counter, 0b00000000, false, 4, 0);

      counter--;

      //have we finished with this setCount ?
      if (counter < 0)
      {
        display1.setSegments(donE); //Display donE on Master
        display2.setSegments(donE); //Display donE on Slave
        delay(1000);

        //stop this TIMER
        setCountTimerFlag = DISABLED;

        //display Master count and colon on first part of Master display.
        display1.showNumberDecEx(MasterCount, 0b01000000, true, 2, 0);

        //display Slave count  on second part of Master display.
        display1.showNumberDec(SlaveCount, true, 2, 2);

        //display Slave count and colon on first part of Slave display.
        display2.showNumberDecEx(SlaveCount, 0b01000000, true, 2, 0);

        //display Master count on second part of Slave display.
        display2.showNumberDec(MasterCount, true, 2, 2);
      }
    }
  }

  //***********************************************
  //are we ready to check for a match ?
  if (newPositionFlag == ENABLED)
  {
    //is there a match between the two values
    if (MasterCount == SlaveCount)
    {
      //enable the countdown timing operation
      setCountTimerFlag = ENABLED;

      //set the counter value
      counter = setCount;

      //disable further checking
      newPositionFlag = DISABLED;
    }
  }

  //***********************************************
  //     Other none blocking code goes here
  //***********************************************

} //END of   loop()


//*******************************************************************************
//switches are checked every 100ms
void checkSwitches()
{
  byte currentState;

  //***********************************************         m a s t e r  P i n
  //masterPin switch
  currentState = digitalRead(masterPin);

  if (lastMasterState != currentState)
  {
    //update to the new state
    lastMasterState = currentState;

    //************************
    //if we are not timing, is the switch pushed ?
    if (setCountTimerFlag == DISABLED && currentState == PUSHED)
    {
      newPositionFlag = ENABLED;

      MasterCount++;  // increment the Master count

      //display Master count and colon on first part of Master display.
      display1.showNumberDecEx(MasterCount, 0b01000000, true, 2, 0);

      //display Slave count  on second part of Master display.
      display1.showNumberDec(SlaveCount, true, 2, 2);

      //display Slave count and colon on first part of Slave display.
      display2.showNumberDecEx(SlaveCount, 0b01000000, true, 2, 0);

      //display Master count on second part of Slave display.
      display2.showNumberDec(MasterCount, true, 2, 2);

      Serial.print("Master: ");
      Serial.print(MasterCount);
      Serial.print(" | Slave: ");
      Serial.print(SlaveCount);
      Serial.println(".");
    }

  } //END of this switch

  //***********************************************         s l a v e P i n
  //slavePin switch
  currentState = digitalRead(slavePin);

  if (lastSlaveState != currentState)
  {
    //update to the new state
    lastSlaveState = currentState;

    //************************
    //if we are not timing, is the switch pushed ?
    if (setCountTimerFlag == DISABLED && currentState == PUSHED)
    {
      newPositionFlag = ENABLED;

      SlaveCount++;

      //display Master count and colon on first part of Master display.
      display1.showNumberDecEx(MasterCount, 0b01000000, true, 2, 0);

      //display Slave count  on second part of Master display.
      display1.showNumberDec(SlaveCount, true, 2, 2);

      //display Slave count and colon on first part of Slave display.
      display2.showNumberDecEx(SlaveCount, 0b01000000, true, 2, 0);

      //display Master count on second part of Slave display.
      display2.showNumberDec(MasterCount, true, 2, 2);

      Serial.print("Master: ");
      Serial.print(MasterCount);
      Serial.print(" | ");
      Serial.print("Slave: ");
      Serial.print(SlaveCount);
      Serial.println(".");
    }

  } //END of this switch

  //***********************************************         r e s e t P i n
  //resetPin switch
  currentState = digitalRead(resetPin);

  if (lastResetState != currentState)
  {
    //update to the new state
    lastResetState = currentState;

    //************************
    //if we are not timing, is the switch pushed ?
    if (setCountTimerFlag == DISABLED && currentState == PUSHED)
    {
      MasterCount = 0;
      SlaveCount = 0;
      Serial.println("Counter Reset.");

      display1.showNumberDecEx(MasterCount, 0b01000000, true, 2, 0);
      display1.showNumberDec(SlaveCount, true, 2, 2);

      display2.showNumberDecEx(SlaveCount, 0b01000000, true, 2, 0);
      display2.showNumberDec(MasterCount, true, 2, 2);
    }

  } //END of this switch

  //***********************************************         i n c r e m e n t
  //increment switch
  currentState = digitalRead(increment);

  if (lastIncrementState != currentState)
  {
    //update to the new state
    lastIncrementState = currentState;

    //************************
    //if we are not timing, is the switch pushed ?
    if (setCountTimerFlag == DISABLED && currentState == PUSHED)
    {
      //enable the TIMER
      adjustingTimerFlag = ENABLED;

      //restart the TIMER
      adjustingMillis = millis();

      setCount++;

      if (setCount > maximumCount)
      {
        setCount = maximumCount;
      }

      //update the displays
      //display setCount Master display.
      display1.setSegments(blank); //clear the Master screen from previous values.
      display1.showNumberDecEx(setCount, 0b00000000, false, 4, 0);

      //display setCount Slave display.
      display2.setSegments(blank); //clear the Slave screen from previous values.
      display2.showNumberDecEx(setCount, 0b00000000, false, 4, 0);

      Serial.print("setCount = ");
      Serial.println(setCount);
    }

  } //END of this switch

  //***********************************************         d e c r e m e n t
  //decrement switch
  currentState = digitalRead(decrement);

  if (lastDecrementState != currentState)
  {
    //update to the new state
    lastDecrementState = currentState;

    //************************
    //if we are not timing, is the switch pushed ?
    if (setCountTimerFlag == DISABLED && currentState == PUSHED)
    {
      //enable the TIMER
      adjustingTimerFlag = ENABLED;

      //restart the TIMER
      adjustingMillis = millis();

      setCount--;

      if (setCount < 0)
      {
        setCount = 0;
      }

      //update the displays
      //display setCount Master display.
      display1.setSegments(blank); //clear the Master screen from previous values.
      display1.showNumberDecEx(setCount, 0b00000000, false, 4, 0);

      //display setCount Slave display.
      display2.setSegments(blank); //clear the Slave screen from previous values.
      display2.showNumberDecEx(setCount, 0b00000000, false, 4, 0);

      Serial.print("setCount = ");
      Serial.println(setCount);
    }

  } //END of this switch

} //END of   checkSwithes()
1 Like

Sorry, i could have been clearer about the sequence.

The delay on the timer would be calculated while preparing the piece, lets say 5 seconds.

I adjust for 5 seconds on the master control, i would see the adjustment in the screen, once done it would flash the display and then show our 00:00 on each of our controls. Only the master control has an adjustment, while both have counter displays and increment buttons. We start our cut, i get to checkpoint one before my partner, push my counter +1 button and i wait. They get there shortly after and press +1 on their control. The program detects that we are both at the same # checkpoint and starts a 5 second countdown. The countdown times out, flashes once then we continue to #2. Again, same countdown time, only counting down once the numbers are the same.

If no delay was needed, the countdown would be set to zero and the program should ignore and countdown timer.

There is a reset button now, but only for the counter. I would add three more buttons to my control for the timer. (+1 second, -1 second and reset).

Possible? As i said, way beyond my skills.

That’s why we need an exact time line.

Things will need to be modified then.


Try the sketch offered you in post #8.

The way it works now.

  • the Master sets the countdown value, say 5 seconds, once set, it stays at that value until it is changed again
  • both counter values are 00
  • Master gets ready to move so they push their switch his/her counter goes to 01, Slave sees Master’s 01
  • Slave pushes her/his switch, it is = 01
  • the two positions are equal so a 5 second countdown happens and times out
  • both positions are displayed again, then a user increments their counter again . . .
  • things repeat

What would you change exactly ?

Thank you so much Larry! I’ll try it tonight and let you know.

You make a statement “we start our cut”.

It’s not clear how the Master tells the Slave to start moving/cutting.

You must explain exactly what needs to happen and when.

Don’t just say we start our cut.

Remember, a computer needs the exact sequence of events.

A point to point sequence needs to be documented.

Example
Step

  1. counters are 00:00
  2. Master sets the countdown time to 5 seconds.
  3. After 2 seconds, 00:00 is displayed, both start cutting/moving
  4. Slave reaches 01 and increments their counter to 01
  5. Master reaches 01 and increments their counter to 01
  6. The 2 counters are equal so the countdown starts and reaches zero.
  7. donE is displayed on both displays
  8. both start cutting again
  9. Master reaches 02, increments their counter
  10. Slave reaches 02, increments their counter, countdown starts again.
  11. donE gets displayed
    . . .

You need to do something like shown above.


EDIT
Also, as the sketch in #8 is written now, my above sequence should suffice, however, how do you tell the other person to initially start cutting ?

Hello again.

Yeah, that cue is usually a verbal or visual cue. As I said originally, these hot-wire cutters are normally pretty simple affairs. Usually just a plug into a variac and the cable separated with the positive running to one handle and the negative to the other, with a Nichrome wire in between as a filament that cuts. The hot-wire cutters I have been making are more just personal projects to make them safer to use and enhance the communication between two operators a distance apart in loud environments. There will still however be a minimum of verbal and visual cues.

I wish I had a video or photo to show you the process in action. Sadly a combination of me having my hands full at the time and signed non-disclosure agreements makes thins impossible.

I am going to connect my prototype shortly and try out your code. I really appreciate your help here. I could never have come up with this on my own.

So Larry, great news!

I put it together on the breadboard and flashed the code, everything does exactly what i needed it to do!!! You are amazing! I definitely owe you a beer or two!

Sadly my “breadboard friendly” buttons don’t seem to make contact with the strips in the breadboard, so i couldn’t film the setting of that, but it does exactly what it should. Thank you so much!

Send me some contact info and i’ll figure out how to get you those cold ones!

P.s.: another sad thing about the video i shot you is that the forum won’t accept video. Doh!

Here we go, I found a small compromise. I managed to fumble and take some progress photos.

The first is the set-up on the breadboard.

The second, the master has been incremented from 1 to 2 and I am preparing to increment the slave by one... 0 to 1 happened so fast I couldn't get a good photo.

Next, slave +1 then the beginning of the countdown...

That's awesome! Thank you so much!

1 Like

This is the same sketch as in #8 but this version has some variable name changes and comments added.

//
//https://forum.arduino.cc/t/adding-a-countdown-timer-to-a-two-click-counters/1050342
//

//********************************************^************************************************
//  Version   YY/MM/DD     Comments
//  =======   ========     ========================================================
//  1.00      22/11/06     Running code
//  1.01      22/11/07     Changed some of the variable names and added some comments
//
//
//********************************************^************************************************


/*
   This is Arduino "Hot-Wire foam cutter checkpoint counter" using a pair of TM1637 seven-segment LED Displays.
   Long pieces of Styrofoam require two people to cut the length. This sketch allows for each user to increment the counter
   as they arrive at each check-point marked on the piece, as well as have a visual read-out showing their location as well as their partner's.

   The original code is based on "Robojax Touch Counter V3 using TM1637 4 digits LED display"
   Link: https://robojax.com/learn/arduino/?vid=robojax_touch_counter_V3_TM1637
*/

#include <Arduino.h>
#include <TM1637Display.h>

//*********************************************************************************
//Master Display connection pins (Digital Pins)
#define CLK1               6
#define DIO1               7

//Slave Display connection pins (Digital Pins)
#define CLK2               8
#define DIO2               9

TM1637Display display1(CLK1, DIO1);      //define display 1 (Master) object
TM1637Display display2(CLK2, DIO2);      //define display 2 (Slave) object

//     01
//  20    02
//     40
//  10    04
//     08

uint8_t blank[]    = { 0x0, 0x0, 0x0, 0x0 };      //data to clear the screen
uint8_t data8888[] = { 0xff, 0xff, 0xff, 0xff };  //show all segments.
uint8_t donE[]     = { 0x5e, 0x5c, 0x54, 0x79 };  //donE

//*********************************************************************************

#define PUSHED             LOW
#define RELEASED           HIGH

#define ENABLED            true
#define DISABLED           false

const byte heartbeatLED  = 13;
const byte resetPin      = 12;
const byte slavePin      = 11;
const byte masterPin     = 10;

//9, 8, 7, and 6 are used for the displays

const byte increment     = 5;
const byte decrement     = 4;

bool timerCountFlag      = DISABLED;  //ENABLE means we are currently counting down the seconds
bool adjustingTimerFlag  = DISABLED;  //Enabled means we are waiting for the user to stop adjusting the target time
bool newPositionFlag     = DISABLED;  //Enabled means either the Master or Slave has reached a milestone

const byte maximumCount  = 10;        //adjust as needed

//the last state the switches were in
byte lastMasterState;
byte lastSlaveState;
byte lastResetState;
byte lastIncrementState;
byte lastDecrementState;

int targetCount;                      //the starting time value the Master has entered for every countdown period
int counter;                          //a decreasing value showing the number of seconds left to go
int MasterCount;                      //the milestone the Master is sitting at
int SlaveCount;                       //the milestone the Slave is sitting at

//timing stuff
unsigned long heartbeatMillis;
unsigned long switchMillis;
unsigned long setCountMillis;
unsigned long adjustingMillis;

//*********************************************************************************
void setup()
{
  Serial.begin(9600);
  Serial.println("Hotwire Checkpoint Counter");

  pinMode(heartbeatLED, OUTPUT);

  pinMode(masterPin, INPUT_PULLUP);
  pinMode(slavePin,  INPUT_PULLUP);
  pinMode(resetPin,  INPUT_PULLUP);
  pinMode(increment, INPUT_PULLUP);
  pinMode(decrement, INPUT_PULLUP);

  display1.setBrightness(0x0f);
  display2.setBrightness(0x0f);

  display1.setSegments(data8888); //display 8888 on display1 for test.
  display2.setSegments(data8888); //display 8888 on display2 for test.
  delay(3000);                    //3 second 8888 display at start-up.

  // Display "00:00" on both displays
  //display Master count and colon on first part of Master display.
  display1.showNumberDecEx(MasterCount, 0b01000000, true, 2, 0);

  //display Slave count  on second part of Master display.
  display1.showNumberDec(SlaveCount, true, 2, 2);

  //display Slave count and colon on first part of Slave display.
  display2.showNumberDecEx(SlaveCount, 0b01000000, true, 2, 0);

  //display Master count on second part of Slave display.
  display2.showNumberDec(MasterCount, true, 2, 2);

} //END of   setup()


//*********************************************************************************
void loop()
{
  //***********************************************         heartbeat T I M E R
  //time to toggle the heartbeat LED ?
  if (millis() - heartbeatMillis >= 500)
  {
    //restart the TIMER
    heartbeatMillis = millis();

    //toggle LED
    digitalWrite(heartbeatLED, !digitalRead(heartbeatLED));
  }

  //***********************************************         check switches T I M E R
  //is it time to check the switches ?
  if (millis() - switchMillis >= 100)
  {
    //restart the TIMER
    switchMillis = millis();

    checkSwitches();
  }

  //***********************************************         adjusting targetCount T I M E R
  //if enabled is it time to once again display Master and Slave milestones ?
  if (adjustingTimerFlag == ENABLED && millis() - adjustingMillis >= 2000ul)
  {
    //2 seconds has gone by without the Master either incrementing or decrementing the targetCount    
    //stop this TIMER
    adjustingTimerFlag = false;

    //re-display the Master and Slave milestones
    //display Master count and colon on first part of Master display.
    display1.showNumberDecEx(MasterCount, 0b01000000, true, 2, 0);

    //display Slave count on second part of Master display.
    display1.showNumberDec(SlaveCount, true, 2, 2);

    //display Slave count and colon on first part of Slave display.
    display2.showNumberDecEx(SlaveCount, 0b01000000, true, 2, 0);

    //display Master count on second part of Slave display.
    display2.showNumberDec(MasterCount, true, 2, 2);

  }

  //***********************************************         counting down T I M E R
  //are we counting down ?
  if (timerCountFlag == ENABLED)
  {
    //has 1 second gone by ?
    if (millis() - setCountMillis >= 1000ul)
    {
      //restart this TIMER
      setCountMillis = millis();

      //update the displays
      //display count on Master display.
      display1.setSegments(blank); //clear the Master screen from previous values.
      display1.showNumberDecEx(counter, 0b00000000, false, 4, 0);


      //display count on Slave display.
      display2.setSegments(blank); //clear the Slave screen from previous values.
      display2.showNumberDecEx(counter, 0b00000000, false, 4, 0);

      counter--;

      //have we finished with this setCount ?
      if (counter < 0)
      {
        display1.setSegments(donE); //Display donE on Master
        display2.setSegments(donE); //Display donE on Slave
        delay(1000);

        //stop this TIMER
        timerCountFlag = DISABLED;

        //display Master count and colon on first part of Master display.
        display1.showNumberDecEx(MasterCount, 0b01000000, true, 2, 0);

        //display Slave count  on second part of Master display.
        display1.showNumberDec(SlaveCount, true, 2, 2);

        //display Slave count and colon on first part of Slave display.
        display2.showNumberDecEx(SlaveCount, 0b01000000, true, 2, 0);

        //display Master count on second part of Slave display.
        display2.showNumberDec(MasterCount, true, 2, 2);
      }
    }
  }

  //***********************************************
  //are we ready to check for a milestone match ?
  if (newPositionFlag == ENABLED)
  {
    //is there a match between the two milestones
    if (MasterCount == SlaveCount)
    {
      //both milestones are now at the same value
      //enable the countdown timing operation
      timerCountFlag = ENABLED;

      //reload the counter with the target value
      counter = targetCount;

      //we do not need to check any further, disable checking
      newPositionFlag = DISABLED;
    }
  }

  //***********************************************
  //     Other none blocking code goes here
  //***********************************************

} //END of   loop()


//*******************************************************************************
//switches are checked every 100ms
void checkSwitches()
{
  byte currentState;

  //***********************************************         m a s t e r  P i n
  //masterPin switch
  currentState = digitalRead(masterPin);

  if (lastMasterState != currentState)
  {
    //update to the new state
    lastMasterState = currentState;

    //************************
    //if we are not timing, is the switch pushed ?
    if (timerCountFlag == DISABLED && currentState == PUSHED)
    {
      newPositionFlag = ENABLED;

      MasterCount++; 

      //display Master count and colon on first part of Master display.
      display1.showNumberDecEx(MasterCount, 0b01000000, true, 2, 0);

      //display Slave count  on second part of Master display.
      display1.showNumberDec(SlaveCount, true, 2, 2);

      //display Slave count and colon on first part of Slave display.
      display2.showNumberDecEx(SlaveCount, 0b01000000, true, 2, 0);

      //display Master count on second part of Slave display.
      display2.showNumberDec(MasterCount, true, 2, 2);

      Serial.print("Master: ");
      Serial.print(MasterCount);
      Serial.print(" | Slave: ");
      Serial.print(SlaveCount);
      Serial.println(".");
    }

  } //END of this switch

  //***********************************************         s l a v e P i n
  //slavePin switch
  currentState = digitalRead(slavePin);

  if (lastSlaveState != currentState)
  {
    //update to the new state
    lastSlaveState = currentState;

    //************************
    //if we are not timing, is the switch pushed ?
    if (timerCountFlag == DISABLED && currentState == PUSHED)
    {
      newPositionFlag = ENABLED;

      SlaveCount++;

      //display Master count and colon on first part of Master display.
      display1.showNumberDecEx(MasterCount, 0b01000000, true, 2, 0);

      //display Slave count  on second part of Master display.
      display1.showNumberDec(SlaveCount, true, 2, 2);

      //display Slave count and colon on first part of Slave display.
      display2.showNumberDecEx(SlaveCount, 0b01000000, true, 2, 0);

      //display Master count on second part of Slave display.
      display2.showNumberDec(MasterCount, true, 2, 2);

      Serial.print("Master: ");
      Serial.print(MasterCount);
      Serial.print(" | ");
      Serial.print("Slave: ");
      Serial.print(SlaveCount);
      Serial.println(".");
    }

  } //END of this switch

  //***********************************************         r e s e t P i n
  //resetPin switch
  currentState = digitalRead(resetPin);

  if (lastResetState != currentState)
  {
    //update to the new state
    lastResetState = currentState;

    //************************
    //if we are not timing, is the switch pushed ?
    if (timerCountFlag == DISABLED && currentState == PUSHED)
    {
      MasterCount = 0;
      SlaveCount = 0;
      Serial.println("Milestones Reset.");

      display1.showNumberDecEx(MasterCount, 0b01000000, true, 2, 0);
      display1.showNumberDec(SlaveCount, true, 2, 2);

      display2.showNumberDecEx(SlaveCount, 0b01000000, true, 2, 0);
      display2.showNumberDec(MasterCount, true, 2, 2);
    }

  } //END of this switch

  //***********************************************         i n c r e m e n t
  //increment switch
  currentState = digitalRead(increment);

  if (lastIncrementState != currentState)
  {
    //update to the new state
    lastIncrementState = currentState;

    //************************
    //if we are not timing, is the switch pushed ?
    if (timerCountFlag == DISABLED && currentState == PUSHED)
    {
      //enable the TIMER
      adjustingTimerFlag = ENABLED;

      //restart the TIMER
      adjustingMillis = millis();

      targetCount++;

      if (targetCount > maximumCount)
      {
        targetCount = maximumCount;
      }

      //update the displays
      //display targetCount on the Master display.
      display1.setSegments(blank); //clear the Master screen from previous values.
      display1.showNumberDecEx(targetCount, 0b00000000, false, 4, 0);

      //display targetCount on the Slave display.
      display2.setSegments(blank); //clear the Slave screen from previous values.
      display2.showNumberDecEx(targetCount, 0b00000000, false, 4, 0);

      Serial.print("targetCount = ");
      Serial.println(targetCount);
    }

  } //END of this switch

  //***********************************************         d e c r e m e n t
  //decrement switch
  currentState = digitalRead(decrement);

  if (lastDecrementState != currentState)
  {
    //update to the new state
    lastDecrementState = currentState;

    //************************
    //if we are not timing, is the switch pushed ?
    if (timerCountFlag == DISABLED && currentState == PUSHED)
    {
      //enable the TIMER
      adjustingTimerFlag = ENABLED;

      //restart the TIMER
      adjustingMillis = millis();

      targetCount--;

      if (targetCount < 0)
      {
        targetCount = 0;
      }

      //update the displays
      //display targetCount Master display.
      display1.setSegments(blank); //clear the Master screen from previous values.
      display1.showNumberDecEx(targetCount, 0b00000000, false, 4, 0);

      //display targetCount Slave display.
      display2.setSegments(blank); //clear the Slave screen from previous values.
      display2.showNumberDecEx(targetCount, 0b00000000, false, 4, 0);

      Serial.print("targetCount = ");
      Serial.println(targetCount);
    }

  } //END of this switch

} //END of   checkSwithes()

In your spare time :wink: , make a more permanent solderless breadboard setup.

image

1 Like

I've been messing with the code a bit on my end too, just playing around to see how things work. I really appreciate your help here, and with your modified code commenting, I will be able to study it and learn a little more.

Thank you so much!

Yeah, a more permanent breadboard set-up would be good. I have about 60 other things I gotta finish first though. So many things on the go.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.