Notifications
Clear all

Exercise Counter

2 Posts
1 Users
0 Reactions
99 Views
tfpfau
(@tfpfau)
Member
Joined: 9 months ago
Posts: 15
Topic starter  

I recently tore a couple of tendons in my shoulder and had to have surgery. I'm now doing physical therapy to try to get my shoulder back into shape.

There are several exercises that I have to do where I move my arm into a position and have to hold it for several seconds. I find myself losing count of the repetitions between counting the hold time and fending off other things that pop into my mind. So I built an ESP32 based exercise counter.

It has a four digit 7 segment LED display and two buttons. Pressing the first button starts and stops the timer. Pressing the second button counts an iteration and stops the timer. Pressing both buttons resets back to zero.

 


This topic was modified 2 weeks ago by tfpfau

   
Quote
tfpfau
(@tfpfau)
Member
Joined: 9 months ago
Posts: 15
Topic starter  

Connections

I'm using a ESP32-S2 Mini, commonly known as the Wemos LOLIN S2 Mini.

ESP32 GPIOs 3, 5, 7, and 11 each connect to the base of a S8550 transistor through a 1k resistor. The collectors of the transistors connect to +3.3V. The emitters each connect to one of the common anodes on the 4-digit 7-segment display.

ESP32 GPIO 16 connects to pin 14 (SER) on the 74HC595. This provides the next bit of data. ESP32 GPIO 33 connects to pin 11 (SRCLK) on the 74HC595. This clocks data into the shift register. ESP32 GPIO 18 connects to pin 12 (RCLK) on the 74HC595. This displays the contents of the shift register. 74HC595 pins 10 (SRCLR) and 16 (VCC) are connected to +3.3V. Pins 8 (GND) and 13 (OE) are connected to ground.

The data output pins of the 74HC595 are connected to the cathode pins on the 4-digit 7-segment display. D0 through D7 are the top segment, the top right segment, the bottom right segment, the bottom segment, the bottom left segment, the top left segment, the center segment, and the decimal point, respectively.

ESP32 GPIOs 2 and 4 connect to one side of the pushbutton switches. This side of the switch is also connected to +3.3V through a 33K resistor and to ground through a 10uf capacitor. The other side of the switch connects to ground through a 220 ohm resistor. The resistors and capacitors can be removed if software debouncing is implemented.

/*
 * Copyright (c) 2026 Thomas Pfau
 * All rights reserved.
 * No warranty expressed or implied.
 */

#define PIN_D0_SEL 3
#define PIN_D1_SEL 5
#define PIN_D2_SEL 7
#define PIN_D3_SEL 11
#define PIN_SHIFT 33
#define PIN_STORE 18
#define PIN_DATA 16
#define PIN_SW_TIMER 2
#define PIN_SW_COUNTER 4

#define SEG_T   0x80
#define SEG_TR  0x40
#define SEG_BR  0x20
#define SEG_B   0x10
#define SEG_BL  0x08
#define SEG_TL  0x04
#define SEG_M   0x02
#define SEG_D   0x01

/* map display digits to gpio number */
int digits[] = { PIN_D0_SEL, PIN_D1_SEL, PIN_D2_SEL, PIN_D3_SEL };

/* define which segments to light for each number */
unsigned int masks[] = {
  SEG_T | SEG_TR | SEG_BR | SEG_B | SEG_BL | SEG_TL,
  SEG_TR | SEG_BR,
  SEG_T | SEG_TR | SEG_M | SEG_BL | SEG_B,
  SEG_T | SEG_TR | SEG_M | SEG_BR | SEG_B,
  SEG_TL | SEG_M | SEG_TR | SEG_BR,
  SEG_T | SEG_TL | SEG_M | SEG_BR | SEG_B,
  SEG_T | SEG_TL | SEG_BL | SEG_B | SEG_BR | SEG_M,
  SEG_T | SEG_TR | SEG_BR,
  SEG_T | SEG_TR | SEG_BR | SEG_B | SEG_BL | SEG_TL | SEG_M,
  SEG_T | SEG_TL | SEG_TR | SEG_M | SEG_BR | SEG_B
};

/* active digit */
int curDigit = 0;

/* current counter value */
unsigned int counter = 0;

/* state of timer switch */
int swTimerState = HIGH;

/* state of counter switch */
int swCounterState = HIGH;

/* time value when timer was started */
long timerStarted = 0;

/* timer state */
bool timerRunning = false;

/* toggle state of timer when its button is pressed */
bool checkTimerSwitch() {
  int state = digitalRead(PIN_SW_TIMER);
  if ( state != swTimerState ) {
    if ( state == LOW ) {
      if ( timerRunning ) {
        timerRunning = false;
      }
      else {
        timerRunning = true;
        timerStarted = millis();
        Serial.println("Timer started");
      }
    }
    swTimerState = state;
  }
  return state == LOW;
}

/* increment counter when its button is pressed */
bool checkCounterSwitch() {
  int state = digitalRead(PIN_SW_COUNTER);
  if ( state != swCounterState ) {
    if ( state == LOW ) {
      ++counter;
      Serial.print("Count = ");
      Serial.println(counter);
      timerRunning = false;
    }
    swCounterState = state;
  }
  return state == LOW;
}

/* stop timer and reset counter to zero */
void resetState() {
  timerRunning = false;
  counter = 0;
}

/* activate shift or store on 595 */
void strobe( int pin ) {
  digitalWrite( pin, HIGH );
  delayMicroseconds(1);
  digitalWrite( pin, LOW );
  delayMicroseconds(1);
}

/* send a data bit to the 595 */
void sendBit( unsigned int value ) {
  digitalWrite( PIN_DATA, value );
  strobe( PIN_SHIFT );  
}

/* send data to display a digit value */
void displayDigitValue( unsigned int value ) {
  unsigned int mask = ~masks[value];
  for ( int i = 0; i < 8; ++i ) {
    sendBit( mask & 1 );
    mask >>= 1;
  }
}

/* select the digit location to store latest output digit */
void selectDigit( int digit ) {
  if ( curDigit )
    digitalWrite( curDigit, HIGH );
  strobe( PIN_STORE );
  curDigit = digits[digit];
  digitalWrite( curDigit, LOW );
}

/* show the timer value in the right hand two digits */
void displayTimerValue() {
  unsigned long value = ( millis() - timerStarted ) / 1000;
  for ( int i = 0; i < 2; ++i ) {
    displayDigitValue(value % 10);
    selectDigit(i);
    value /= 10;
    if ( value == 0 )
      break;
  }
}

/* display the counter value in the left hand two digits */
void displayCounterValue() {
  unsigned int value = counter;
  for ( int i = 2; i < 4; ++i ) {
    displayDigitValue(value % 10);
    selectDigit(i);
    value /= 10;
    if ( value == 0 )
      break;
  }
}

/* initialization */
void setup() {
  Serial.begin(115200);

  pinMode( PIN_D0_SEL, OUTPUT );
  digitalWrite( PIN_D0_SEL, HIGH );

  pinMode( PIN_D1_SEL, OUTPUT );
  digitalWrite( PIN_D1_SEL, HIGH );

  pinMode( PIN_D2_SEL, OUTPUT );
  digitalWrite( PIN_D2_SEL, HIGH );

  pinMode( PIN_D3_SEL, OUTPUT );
  digitalWrite( PIN_D3_SEL, HIGH );

  pinMode( PIN_SHIFT, OUTPUT );
  digitalWrite( PIN_SHIFT, LOW );

  pinMode( PIN_STORE, OUTPUT );
  digitalWrite( PIN_STORE, LOW );

  pinMode( PIN_DATA, OUTPUT );
  digitalWrite( PIN_DATA, LOW );

  pinMode( PIN_SW_COUNTER, INPUT );
  pinMode( PIN_SW_TIMER, INPUT );
}

void loop() {
  /* check for activation of switches. reset state if both are pressed */
  if ( checkCounterSwitch() & checkTimerSwitch() )
    resetState();
  /* display timer value if timer is running */
  if ( timerRunning )
    displayTimerValue();
  /* display counter value */
  displayCounterValue();
}

This post was modified 2 weeks ago 3 times by tfpfau

   
ReplyQuote