icon

DualSport Tracker – Sync Your Biking and Running Routes

 

Introduction: What is DualSport Tracker?

 

Hi! My name is Vicenzo, a 15-year-old teenager who enjoys art and sports, especially

 mountain biking and running. After a bike ride through the forests where I usually train, I asked myself: "How many kilometers have I covered, and how many calories have I burned?" I wanted to be more aware of my physical performance.

 

While searching for a solution, I came across several GPS apps that promised to track this information. However, in the areas where I train, the GPS signal is very weak or nonexistent, making these apps useless. That’s when I decided that instead of relying on limited technology, I could design my own solution.

 

Thus, the DualSport Tracker was born. My goal was to create a device that could measure the distance traveled and calories burned on a bike ride without using GPS. But why stop there? I also added the ability to measure time and speed, and even included a heart rate sensor to monitor my health during exercise. Additionally, I wanted the device to be portable, easy to remove from the bike, and also usable as a pedometer while running. In short, my DualSport Tracker combines my two favorite sports into one multifunctional device. All of this is possible thanks to the power of the Unihiker board, which serves as the brain of the device.ç

 

How Does DualSport Tracker Work?

 

The first question you might have is: "If you don't use GPS, how do you measure distance, speed, and other factors?" The heart of the system is a Hall effect sensor, which, combined with a magnet attached to the bike wheel, detects every time the wheel completes a revolution. Since the perimeter of the wheel is constant, we can multiply the number of revolutions by the perimeter to calculate the total distance traveled.

 

The Hall effect sensor acts as a proximity detector. Every time the magnet passes near the sensor, it generates a signal, indicating that the wheel has completed a revolution. Using this information, the device calculates distance, speed, and calories burned. I also added a KY-039 heart rate sensor to provide a more complete picture of physical exertion by monitoring my heart rate during exercise.

 

In summary, the DualSport Tracker has the following features:

 

Key Features of the DualSport Tracker:

 

Key Features of the DualSport Tracker:

Distance Measurement:Bike mode: Uses a Hall effect sensor and the wheel’s perimeter to calculate the distance traveled.Pedometer mode: Uses an accelerometer to count steps and calculate distance by multiplying the steps by an average stride length.Speed Calculation (only in bike mode):Bike mode: Calculates speed in km/h by measuring the time between each wheel revolution.Heart Rate Monitor (only in bike mode):Bike mode: Uses the KY-039 sensor to measure beats per minute (BPM), providing data on physical exertion.Calories Burned:Bike mode: Estimates calories burned based on distance traveled and heart rate.Pedometer mode: Calculates calories burned based on the number of steps taken and an estimated calorie-per-step value.Versatility:The device can be detached from the bike and used as a pedometer, making it suitable for both cycling and running or walking.

Without further ado, let’s start building!

Materials Needed:

 

3D printed parts (attached files)Unihiker boardHall effect sensorKY-039 heart rate sensorCylindrical portable battery (Power bank)USB type A to USB type C cableM3 x 3 cm screw with nutDupont cablesZip tiesElectrical tape
STEP 1
Base Assembly:

Once the 3D printed parts are ready, start by assembling the base, which includes the Unihiker board, the portable battery, and the USB cable. This step is crucial for both the pedometer and bike modes. You can follow the attached video for guidance during the assembly. This base securely connects to the bike or can be used directly as a pedometer.

icon printables.zip 19KB Download(6)
STEP 2
Bike Assembly:

For bike mode, install the sensors (Hall effect sensor and heart rate sensor), the magnet, and the mount for the Unihiker board. The Hall effect sensor is mounted on the bike fork, while the magnet is attached to one of the wheel spokes with zip ties. Make sure to secure the sensors and cables with electrical tape for added safety. Connect the Hall sensor to pin 22 and the heart rate sensor to pin 21 through the Unihiker's external ports.

STEP 3
Project Code

In this section, I’ll explain how the code works so that you can understand the calculations behind the device. I have commented on each part of the code to make it easier to follow and modify if needed. Here’s an overview of how sensor readings are handled in both modes.

 

Bike mode: The Hall effect sensor detects each time the wheel completes a revolution. With this information, we calculate speed by dividing the distance traveled by the time elapsed between revolutions. Based on the speed and distance, we estimate the calories burned during the activity. Pedometer mode: The accelerometer integrated into the board detects steps while running or walking. Based on an average stride length, we calculate the distance traveled. Calories burned are estimated based on the number of steps and an approximate calorie-per-step value. 

Mathematical calculations used in the device:

Speed (km/h) = (Wheel perimeter / time per revolution) * 3.6Calories burned (bike) = Distance traveled (km) * 40Distance traveled (pedometer) = Number of steps * average stride length 

The complete code is attached and available in both English and Spanish versions for download from GitHub. To load the code onto your device and add image files, I recommend using Mind+, the editor I used for this project.

CODE
#general bike mode code

from unihiker import GUI
from pinpong.board import Board, Pin
import time

# Initialize the board
Board().begin()

# Create an instance of the GUI
u_gui = GUI()

# Define sensor pins
heart_sensor_pin = Pin(Pin.P21, Pin.ANALOG)  # Heart rate sensor on Pin 21
bike_sensor_pin = Pin(Pin.P22, Pin.ANALOG)  # Bike sensor on Pin 22

# Parameters for the bike
wheel_perimeter = 1000  # in meters (adjust according to your wheel size)
revolutions = 0
distance_traveled = 0.0  # in kilometers
speed = 0.0  # in km/h
max_speed = 0.0  # in km/h
calories_burned = 0.0
start_time = 0
previous_time = 0
previous_bike_sensor_value = 512
active = False

# Parameters for the heart rate
last_beat_time = time.time()
heart_rate_BPM = 0
is_peak = False
min_time_between_beats = 300  # Minimum time between beats in milliseconds
max_time_between_beats = 2000  # Maximum time between beats
change_threshold = 100  # Significant change between readings to detect a beat
previous_heart_sensor_value = 0

# Functions for the bike
def calculate_calories(distance):
    return distance * 40  # adjust for moderate activity

def calculate_speed(lap_time):
    global wheel_perimeter
    if lap_time > 0:
        speed_m_s = wheel_perimeter / lap_time
        return speed_m_s * 3.6  # Convert from m/s to km/h
    else:
        return 0.0

# Functions for managing the bike ride
def on_buttona_click_callback():
    global active, previous_time, start_time, revolutions, distance_traveled, max_speed, calories_burned
    active = True
    start_time = time.time()
    previous_time = time.time()
    revolutions = 0
    distance_traveled = 0.0
    max_speed = 0.0
    calories_burned = 0.0
    u_gui.clear()

def on_buttonb_click_callback():
    global active
    active = False
    u_gui.clear()
    total_time = time.time() - start_time
    total_time_min = total_time / 60  # Convert to minutes
    u_gui.draw_text(text=f"Trip Finished!", origin="center", x=120, y=20, font=("Arial", 20, "bold"), color="#FF0000")
    u_gui.draw_text(text=f"Distance: {distance_traveled:.2f} km", x=5, y=50, font=("Arial", 16), color="#00FF00")
    u_gui.draw_text(text=f"Max Speed: {max_speed:.2f} km/h", x=5, y=70, font=("Arial", 16), color="#0000FF")
    u_gui.draw_text(text=f"Calories: {calories_burned:.2f}", x=5, y=90, font=("Arial", 16), color="#FF00FF")
    u_gui.draw_text(text=f"Time: {total_time_min:.2f} min", x=5, y=110, font=("Arial", 16), color="#FFAA00")
    u_gui.draw_text(text="Press A to restart", origin="center", x=120, y=160, font=("Arial", 12), color="#000000")
    u_gui.draw_image(x=-15, y=190, w=350, h=150, image='bici1.png')

u_gui.on_a_click(on_buttona_click_callback)
u_gui.on_b_click(on_buttonb_click_callback)

# Display initial message
u_gui.clear()
u_gui.draw_text(text="Press A to", origin="center", x=120, y=50, font=("Arial", 18, "bold"), color="#7ed957")
u_gui.draw_text(text="start your trip", origin="center", x=120, y=80, font=("Arial", 18, "bold"), color="#7ed957")
u_gui.draw_image(x=-15, y=120, w=350, h=150, image='bici3.png')

# Function to detect heartbeat
def detect_heartbeat(sensor_value):
    global is_peak, last_beat_time, heart_rate_BPM, previous_heart_sensor_value
    
    current_time = time.time()
    time_diff = (current_time - last_beat_time) * 1000  # Convert to milliseconds
    
    if abs(sensor_value - previous_heart_sensor_value) > change_threshold and not is_peak:
        if min_time_between_beats < time_diff < max_time_between_beats:  # Consider reasonable times
            is_peak = True
            heart_rate_BPM = 60000 / time_diff  # Calculate BPM
            last_beat_time = current_time
            print(f"Heartbeat detected. Interval: {time_diff} ms. Calculated BPM: {heart_rate_BPM}")
    elif abs(sensor_value - previous_heart_sensor_value) < change_threshold and is_peak:
        is_peak = False  # Prepare to detect the next heartbeat

    previous_heart_sensor_value = sensor_value  # Update the last read value

# Main loop
while True:
    if active:
        # --- Calculations for the bike ---
        bike_sensor_value = bike_sensor_pin.read_analog()
        
        if bike_sensor_value < 512 and previous_bike_sensor_value >= 512:
            current_time = time.time()
            lap_time = current_time - previous_time
            previous_time = current_time
            
            speed = calculate_speed(lap_time)
            revolutions += 1
            distance_traveled = revolutions * wheel_perimeter / 1000  # Convert to km
            
            if speed > max_speed:
                max_speed = speed
            
            calories_burned = calculate_calories(distance_traveled)
        
        # Calculate elapsed time
        total_time = time.time() - start_time
        total_time_min = total_time / 60  # Convert to minutes
        
        # --- Calculations for heart rate ---
        heart_sensor_value = heart_sensor_pin.read_analog()
        detect_heartbeat(heart_sensor_value)
        
        # Display information on screen
        u_gui.clear()
        u_gui.draw_text(text=f"Speed: {speed:.2f} km/h", x=5, y=10, font=("Arial", 16), color="#0000FF")
        u_gui.draw_text(text=f"Distance: {distance_traveled:.2f} km", x=5, y=40, font=("Arial", 16), color="#00FF00")
        u_gui.draw_text(text=f"Calories: {calories_burned:.2f}", x=5, y=70, font=("Arial", 16), color="#FF00FF")
        u_gui.draw_text(text=f"H.R.: {heart_rate_BPM:.2f} BPM", x=5, y=100, font=("Arial", 16), color="#FF0000")
        u_gui.draw_text(text=f"Time: {total_time_min:.2f} min", x=5, y=130, font=("Arial", 16), color="#FFAA00")
        u_gui.draw_image(x=-15, y=170, w=350, h=150, image='bici2.png')
        
        previous_bike_sensor_value = bike_sensor_value
    
    time.sleep(0.5)
STEP 4
Ready to Use Your DualSport Tracker!

Congratulations! You have assembled your DualSport Tracker, and now it's ready to use. You can use it on your bike rides or during your running sessions. The main menu will allow you to easily switch between bike and pedometer modes, depending on the exercise you prefer.

The device will display key real-time information such as speed, distance traveled, calories burned, heart rate, and total exercise time. If you want to switch from bike mode to pedometer mode, simply disconnect the two sensors and detach the device from the bike (this is easy thanks to the simple design of the device). To mount it back on the bike, just reverse the process.

 

 

Using the Menu

 

Start Activity: Press button 'A' to start recording your activity.End Activity: Press button 'B' to stop recording and display the total data of your exercise, including distance, max speed, calories burned, and time.Switch Modes: Use the menu to toggle between bike and pedometer modes depending on the activity you’re doing.

 

 

Conclusion and Potential of the DualSport Tracker

 

The DualSport Tracker has great potential for those who enjoy mountain biking, running, or simply want to monitor their physical activity without relying on GPS or internet connections. The flexibility of the device, which allows it to be used both while cycling and running, and the ability to customize its parameters according to the user’s needs, make it a practical and versatile tool, ideal for both athletes and makers.

 

This project combines technology, creativity, and a passion for sports, providing an efficient solution for measuring distances, speeds, calories burned, and heart rate. It's perfect for those who train in areas without GPS coverage or are looking for a portable and easy-to-use tool to improve their performance.

 

I encourage you to experiment with the DualSport Tracker, adjust it to your needs, and explore new features. If you have any questions or ideas for your own version of the device, I would be happy to help. Without further ado, see you next time!

 

License
All Rights
Reserved
licensBg
1