Image of eBits Academy robotics illustration

Become the Next Usain Bolt | Sprint Tracker |

  • September 21, 2022
  • |
  • Jesper Nielsen

Want to be the next Usain Bolt? Take a look! This project measures how quickly you can run 50, 100, 150 ... or 500 metres. All you need is an ESP TTGO, ultrasonic sensor and a couple of buttons from the component mega kit — current selection; check compatibility. The table below also suggests a shopping list that covers what you need for this project and many future ones!

  • Specifications
  • Building the Circuit on a Breadboard
  • Programming the ESP32 TTGO
  • 3D-Printed Enclosure
  • Implementation
  • Specifications

    To build this project, you will need the components below. They are all available here at eBits.dk.

     

    You need the following items from the component mega kit — current selection; check compatibility kit:

    Quantity Component
    1 Active fixed-frequency buzzer
    3 Buttons
    3 10 kΩ resistors
    12 Dupont cables

      

    Building the Circuit on a Breadboard

    Build the breadboard circuit by connecting the modules to the TTGO. If you follow the table below, you do not need to change the code further down the page or at the following link: 

    HC-SR04 VCC

    TTGO 5V

    HC-SR04 GND

    TTGO GND

    HC-SR04 TRIG

    TTGO 32

    HC-SR04 ECHO

    TTGO 33 via suitable 5V→3.3V level shifter or voltage divider (never direct 5V)

    Buzzer +

    TTGO 17

    Buzzer -

    TTGO GND

    Start button

    TTGO 25

    Up button

    TTGO 26

    Down button

    TTGO 27

    The image below illustrates the breadboard setup. The original diagram may not show level adaptation: HC-SR04 ECHO can be 5V and must be reduced to a safe 3.3V level before ESP32 GPIO33. 

    The image below shows the same setup, but with a battery — current selection; check compatibility and a charging circuit added so the unit can become portable. This arrangement connects directly to the back of the TTGO which supports direct connection of a battery — current selection; check compatibility. The charging circuit accepts USB-C at 5V 2A. The battery shown in the pictures is an 1800 mAh battery — current selection; check compatibility that should provide a reasonable operating time. Check battery polarity, protection, and charging specifications. Do not connect two charging outputs to the same cell without an approved charging topology; check the TTGO board’s built-in charger before using an additional charging module.

    The image below illustrates the breadboard setup. The original diagram may not show level adaptation: HC-SR04 ECHO can be 5V and must be reduced to a safe 3.3V level before ESP32 GPIO33. 

    Programming the TTGO T-Display

    The code snippet below shows the libraries used in this project, mainly for accessing the display on the TTGO. You can download the folders from this link: TFT_eSPI.h & Free_Fonts.h. 
    Place them in the libraries folder under Documents -> Arduino -> libraries.

    #include "Free_Fonts.h" // Include the header file attached to this sketch
    #include "SPI.h"
    #include "TFT_eSPI.h"
    
    //Based on TTGO
    #define button1 26 
    #define button2 27 
    #define start_run 25
    #define TRIG 32
    #define ECHO 33
    #define A0 17   
    The code snippet below is the main loop, which lets you select the distance to measure and checks whether start_run has been pressed.

    void loop() {
      tft.setTextDatum(MC_DATUM);
      tft.setTextColor(TFT_WHITE, TFT_BLACK);
    
      if(digitalRead(start_run) == HIGH){
      if(timesDisplayed == false){
      PrepareForRun();
      }else{
        timesDisplayed = false;
        DisplayStartMenu(distance);
        delay(1000);
      }
    }
      if(digitalRead(button2) == HIGH){
      if(distance > 50){
      distance-=50;
      UpdateDistance(distance);
      delay(200);
      }
    }
    if(digitalRead(button1) == HIGH){
      if(distance < 500){
      distance+=50;
      UpdateDistance(distance);
      delay(200);
      }
    }   
    }

    To update the screen when the distance buttons are pressed, the following display update function is called.

    void UpdateDistance(int _distance){
      tft.fillScreen(TFT_BLACK);
      String text = itoa(_distance, buf, 10);
      text += "m";
      tft.setFreeFont(FF2);
      tft.drawString("Distance: "+ text, 120, 40, GFXFF);  
    }

    Pressing the start_run button calls the following function, which displays a countdown and plays an audible countdown of beeps. When the countdown ends, the buzzer emits a longer tone to indicate that timing has started.

    void PrepareForRun(){
      int countdown = distance/5;
      digitalWrite(A0, HIGH);
        delay(200);
        digitalWrite(A0, LOW);
        delay(200);
        digitalWrite(A0, HIGH);
        delay(200);
        digitalWrite(A0, LOW);
        delay(200);
      for(int a = 0; a < distance/5; a++){
        --countdown; 
        String text = itoa(countdown, buf, 10);
        tft.fillScreen(TFT_BLACK);
        tft.setFreeFont(FF2);
        tft.drawString("Go to start: "+ text, 120, 40, GFXFF);
       delay(1000);
      }
      for(int b = 3; b > 0; b--){
        String text = itoa(b, buf, 10);
        tft.fillScreen(TFT_BLACK);
        tft.setFreeFont(FF2);
        tft.drawString("Time: "+ text, 120, 40, GFXFF);
        digitalWrite(A0, HIGH);
        delay(500);
        digitalWrite(A0, LOW);
        delay(500);
      }
      digitalWrite(A0, HIGH);
      delay(2000);
      tft.fillScreen(TFT_BLACK);
        tft.setFreeFont(FF2);
        tft.drawString("RUN!", 120, 40, GFXFF);
      digitalWrite(A0, LOW);
      MeasureRunTime();
      }

    When you run past the ultrasonic sensor MeasureRunTime() completes, and the time is passed to DisplayTimes(double timer).

    void MeasureRunTime(){
      timeMeasuring = true;
      runTimeStart = millis();
      while(MeasureDistance() > 100){
        delay(10);
      }
      runTimeMs = millis() - runTimeStart;
      double timer = (double)runTimeMs/1000;
      Serial.print("Time: ");
      Serial.println((double)runTimeMs/1000);
      timeMeasuring = false;
      DisplayTimes(timer);
    }

    Here is the function called in the while loop until it is broken.

    float MeasureDistance(){
      digitalWrite(TRIG, LOW);
      delayMicroseconds(2);
      digitalWrite(TRIG, HIGH);
      delayMicroseconds(5);
      digitalWrite(TRIG, LOW);
    
      float t = pulseIn(ECHO, HIGH);
      float distance = t*0.017015;
      Serial.print("Distance: ");
      Serial.println(distance);
      return distance;
    }

    The code snippet below is another screen update function, this time displaying the latest run time.

    void DisplayTimes(int time_number){
      String text = itoa(time_number, buf, 10);
      tft.fillScreen(TFT_BLACK);
        tft.setFreeFont(FF2);
        tft.drawString("Time: "+ text +" s", 120, 40, GFXFF);
      }

    The entire project is available on eBits GitHub

    3D-Printed Enclosure

    The images below show the enclosure design and the intended arrangement of the modules inside. If you do not have a 3D printer at home, many online printing services offer this at an affordable price. 
    The design below is available here

     

     

    Implementation

    Here is an image of the connections and wiring of the charging circuit, buttons, TTGO and HC-SR04 on the lid of the 3D-printed enclosure. A small dab of superglue around the edges helps secure the buttons. The rest can be mounted with screws.

     

    Here is the finished, fully functional sprint tracker. The USB-C charging port is at the bottom, with an on/off switch mounted at the top. The enclosure is quite small and easy to take with you.

    Leave a comment

    Please note, comments need to be approved before they are published.