End-To-End Workout

by danielle_levy in Circuits > Wearables

167 Views, 1 Favorites, 0 Comments

End-To-End Workout

IMG_0996.jpg
green.jpg
unnamed.jpg
Screenshot 2025-04-01 191649.png

Our project is called "End-To-End Workout" because we are enabling a full end-to-end experience when you want to workout. From enabling your workout session through Siri, and enabling sets counting of different workouts, to turn on the water tank and lights in your shower that will make your workout exprience much easy and comfortable!

End-to-end workout is a smart fitness tracking system that monitors workout using an accelerator (IMU sensor). By detecting direction changes in the y axis, it counts push-ups and sit-ups during a trainee's workout. We've integrated Blynk to display real-time workout progress and control various features remotely. The system also connects to a Control4 automation system, allowing users to activate devices such as water tanks and lights during their workout. Workout data is sent to a Telegram bot via an HTTP request, providing users with an automated summary of their progress.

In addition End-To-End Workout can record sets separetally, and summarize each set to the Telegram bot. For the set transition our system uses Siri voiceover so the trainee would keep concentrate on the workout.

For detailed explanation of the usage of End-To-End Workout please watch the following video that we created:

video link

Further more we attached the full code and its dependencies for the End-To-End Workout but we recommend to go over every step in this tutorial and write the code as followed in the detailed steps.

Supplies

suplly1.JPG
supply2.JPG
supply3.JPG

For the End-to-End Workout (circuit and the box that contains the circuit) we will need

  1. esp32
  2. speaker
  3. IMU sensor (accelerator)
  4. led strip
  5. blue led
  6. 4 AA baterries
  7. cutting board
  8. glue
  9. isolierband
  10. utility knife
  11. cardboard
  12. ruler
  13. two sided masking tape
  14. strap with buckles

Building the Circuit

circuit.JPG

First we'll start by building the esp32 circuit of the End-To-End Workout:

  1. connect the speaker to pin IO26
  2. connect the led strip to pin IO16
  3. connect the IMU sensor to: SCL (IO22), SDA (IO21)
  4. connect the blue led to pin IO15
  5. connect the 4 AA batteries to the esp32 power

Building the Strapped Wrapping Box

IMG_0963.jpg
IMG_0965.jpg
IMG_0967.jpg
IMG_0966.jpg
IMG_0968.jpg
IMG_0997.jpg
IMG_0996.jpg

Note: you can skip this part if you have a suitable wrapping box for the esp32 circuit

In case you don't have any suitable wrapping box let's make one!

Take the cutting board, ruler, utility knife and cardboard.

First cut 2 rectangles from the cardboard, the measurements for those rectangles are 16 X 14 cm. In one of those rectangles cut a hole for the led strip diagonally, this will be the upper rectangle of the wrapping box.

Then cut another 2 rectangles from the cardboard, the measurements for those rectangles are 16 X 5 cm. In one of those rectangles cut a hole for the blue led, try cutting it near the edge of the rectangle so the trainee could see it.

Now cut another 2 rectangles from the cardboard, the measurements for those rectangles are 14 X 5 cm.

Next take the 16 X 14 cm rectangle without the hole for the led strip and place the esp32 circuit from step 1 on it, and using the isolierband stick the esp32 circuit to it.

Now take the other 16 X 14 cm rectangle and stick the led strip to the hole in it using the isolierband. In addition insert the blue led to the hole in the 16 X 5 cm rectangle.

Next glue every rectangle (except the other 16 X 14 cm rectangle) to the base 16 X 14 cm rectangle using the glue.

Now notice that before you glue the last 16 X 14 cm rectangle to the wrapping box make sure to turn on the esp32 and then glue it.

Next we need to connect the strap to the wrapping box. Put 2 pieces of two sided masking tape on the back of the wrapping box and stick the strap to it.

That's it, you just built the strapped wrapping box for the End-To-End Workout!

Connect the Esp32 to Wifi

In order to implemet all the features in End-To-End Workout you need to connect the esp32 to a wifi.

First add those modules

#include <HTTPClient.h>
#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiClientSecure.h>

Next add those variables

// wifi configuration
char ssid[] = "your_wifi_ssid";
char pass[] = "your_wifi_password";

Create the following function

void connectToWifi()
{
// connecting to wifi
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, pass);
Serial.print("Connecting to wifi...");
while (WiFi.status() != WL_CONNECTED)
{
Serial.print(".");
delay(1000);
}
Serial.println("successfully connected to wifi");
}

Then you need to connect to the wifi so add this to your setup function

// add this to your setup function
connectToWifi();

Integrating With Control4 System

control4.jpg

Intro: Control4 is a sophisticated home automation system that integrates various household devices into one unified platform, providing users with enhanced control over their lighting, audio, video, climate, and security systems. Additionally, it supports voice commands through compatibility with popular voice assistants, and offers robust third-party integration, allowing for seamless operation with a wide range of smart home devices.

In case you don't have control4 system, that's fine, you can skip to step 2.


How to integrate?


Step 1:

Send a POST request to:

url: https://apis.control4.com/authentication/v1/rest
payload:
{
"clientInfo": {
"device": {
"deviceName": "pyControl4",
"deviceUUID": "0000000000000000",
"make": "pyControl4",
"model": "pyControl4",
"os": "Android",
"osVersion": "10"
},
"userInfo": {
"applicationKey": "78f6791373d61bea49fdb9fb8897f1f3af193f11",
"password": {your_control4_password},
"userName": {your_control4_user_name}
}
}
}


In the response, extract the "token" field.

Step 2:

Send a GET request to get all controllers registered to an account using the bearer token from Step 1:

url: https://apis.control4.com/account/v3/rest/accounts
token: Bearer Token from step 1

Take the "controllerCommonName" field from the response, which should look like "control4_xxx_xxxxx".

Step 3:

Send a POST request to get the director bearer token for full application control, using the bearer token from Step 1 and the controller name from Step 2.

url: https://apis.control4.com/authentication/v1/rest/authorization
token: Bearer Token from step 1
payload:
{
"serviceInfo": {
"commonName": {controller_common_name},
"services": "director"
}
}

Extract the "authToken" from the response; this will be used for triggering actions in your smart home.


Step 4: Control4 IP

To get your Control4 IP, open the app, click on "Settings" → "More", and find the "IP Address" such as "xxx.xxx.xx.xx".


Step 5: Get Your Relevant Items

Because each house has different component names, retrieve item IDs by sending a GET request using the bearer token from Step 3:

url: https://{your_control4_ip_address}/api/v1/items
token: Bearer Token from step 3


Find your sun-heated water tank by its known name under the "name" field and retrieve its "id" for further actions.


Also, please find your Bathroom light name in the items and save it's id in the relevant place in the ino file.

First add those variables

// control4 configuration
// fill your control4 details
const char *control4LocalUrl = "https://your_wifi_ip_address/api/v1/items/";
const char *bearerToken = "your_control4_bearer_token"; // add your control4 bearer token

// control4 properties ids
const char *waterTankId = "219";
const char *lightInShowerId = "229";

Now add those functions

// turn on water tank via control4
void turnOnWaterTank()
{
triggerControl4OnOff(waterTankId, 1);
}

// turn on light in shower via control4
void turnOnLightInShower()
{
triggerControl4OnOff(lightInShowerId, 1);
}


// send post request to set control4 item state
void triggerControl4OnOff(const char *itemId, int isON)
{
WiFiClientSecure *client = new WiFiClientSecure;
if (client)
{
client->setInsecure();
{
HTTPClient https;
if (https.begin(*client, String(control4LocalUrl) + itemId + "/commands"))
{
// adding headers
https.addHeader("Content-Type", "application/json"); // Set JSON header
https.addHeader("Authorization", "Bearer " + String(bearerToken));
String postPayload = "";
// edit the request payload according to isOn variable
if (isON)
{
String postPayload = "{\"async\": true, \"command\": \"ON\", \"tParams\": {}}";
}
else
{
String postPayload = "{\"async\": true, \"command\": \"OFF\", \"tParams\": {}}";
}
// sending post request
int httpCode = https.POST(postPayload);
// handle post response
if (httpCode > 0)
{
Serial.printf("POST response code: %d\n", httpCode);
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_CREATED)
{
String response = https.getString();
Serial.println("Response: " + response);
}
}
// post request failed
else
{
Serial.printf("POST request failed, error: %s\n", https.errorToString(httpCode).c_str());
}
https.end();
}
else
{
Serial.println("Unable to connect to control4");
}
}
delete client;
}
else
{
Serial.println("Unable to create https client");
}
}

Creating Telegram Bot

Telegram-logo-Featured.jpg
telegram bot 1.png
telegram bot 2.png

In this step we'll create a Telegram bot for sending the workout summary to the user. We'll use BotFather, which is a bot created by Telegram to make bot creation easier. With BotFather, we can create our own bot by following a few simple steps:

Step 1: Open the Telegram app on your computer

To create a Telegram bot, you'll need to have the Telegram app installed on your computer. If you don't have it already, you can download it from the Telegram website.

Step 2: Connect to BotFather

To connect to BotFather, search for "@BotFather" in the Telegram app and click on the result to start a conversation.

Step 3: Select the New Bot option

In the conversation with BotFather, select the "New Bot" option to start creating your new bot. BotFather will guide you through the rest of the process.

Step 4: Add a bot name

Next, BotFather will ask you to provide a name for your bot. Choose a name that accurately reflects the purpose of your bot and is easy to remember, we chose "workout summarizer".

Step 5: Choose a username for your bot

Lastly, BotFather will ask you to choose a username for your bot. This username will be used to create a unique URL that people can use to access your bot. Choose a username that is easy to remember and related to your bot's purpose, we chose "workout_summarizer_bot".

And that's it! With these five simple steps, you can create your very own Telegram bot that you will use to send the workout summarize to the user.

Now you need to extract the token from the last message that BotFather has sent you (see attached image) so you can use it to send requests to the telegram API that will send messages to the user via the bot.

In Addition, in order to send messages to the user via the telegram bot we need to get the telegram chat id of the user.

Follow these steps in order to get the user's chat id:

  1. Search and open our new Telegram bot
  2. Click Start or send a message
  3. Open this URL in a browser https://api.telegram.org/bot{our_bot_token}/getUpdates
  4. We need to prefix our token with a word bot
  5. eg: https://api.telegram.org/bot63xxxxxx71:AAFoxxxxn0hwA-2TVSxxxNf4c/getUpdates
  6. We will get a json like this:
{
"ok": true,
"result": [
{
"update_id": 83xxxxx35,
"message": {
"message_id": 2643,
"from": {...},
"chat": {
"id": 21xxxxx38,
"first_name": "...",
"last_name": "...",
"username": "@username",
"type": "private"
},
"date": 1703062972,
"text": "/start"
}
}
]
}
  1. Check the value of result.0.message.chat.id, and the above json this is our chat id: 21xxxxx38

So now we have both the token of the bot and the chat id of the user.

Creating a Make.com Automation for the Telegram Bot

images.jpg
flow chart.png
run immediately.png
telegramBotPhoto.png
webhook pic.png
webhookPhoto.png

First you will need to create an account in make.com. Next we need to create a new scenario, in your make account go to the Scenarios section and create a new scenario.

Now we need to create a new Webhook, choose a name for the webhook, we chose "summarize workout".

Next you will receive an URL, we will use it to send the workout data from the esp32 to the telegram bot.

Now you need to add to the scenario the Telegram Bot app and configure it:

First we need to create a connection to the telegram bot using the token from step 2 (see attached photo).

Secondly we need to configure the chat id of the user using the chat id that we got from step 2 (see attached photo).

Lastly we need to create the workout summary message that the telegram bot will send to the user:

"Hello! I am your workout summarizer bot!

Here is your workout summary:

Set number: {{2.sets}}

Number of Push-Ups: {{2.pushUps}}

Number of Sit-Ups: {{2.sitUps}}"

This way the workout data that the esp32 will send to Make can be used in the summary message.

Now you need to connect the Webhook module to the Telegram Bot module, drag the plus button in the Webhook module and connect it to the Telegram Bot module (see attached photo).

Next you need to turn on the switch button "immediately as data arrives" (see attached photo).

Now you need to extract the Make.com url of the webhook, click on the webhook in your scenario and copy the address (see attached photo).

Next you can send your workout data to Make.com url (that you just copied) using a GET request with the parameters sets, pushUps and sitUps as seen in the code below.

First add those variables

// Make.com constants
const char *makeUrl = "https://hook.eu2.make.com/{your_make_url}?";
const char *sitUpsSuffix = "&sitUps=";
const char *pushUpsSuffix = "pushUps=";
const char *lapsSuffix = "&laps=";

Then add this function

// send a single set summary to telegram bot
void sendWorkoutDataToTelegramBot(int pushUpsNum, int sitUpsNum, int sets)
{
WiFiClientSecure *client = new WiFiClientSecure;
if (client)
{
client->setInsecure();
{
HTTPClient https;
if (https.begin(*client, String(makeUrl) + String(pushUpsSuffix) + pushUpsNum + String(sitUpsSuffix) + sitUpsNum + String(setsSuffix) + sets))
{
// send get request
int httpCode = https.GET();
// handle get response
if (httpCode > 0)
{
Serial.printf("GET response code: %d\n", httpCode);
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY)
{
String payload = https.getString();
Serial.println(payload);
}
}
// get request failed
else
{
Serial.printf("GET request failed, error: %s\n", https.errorToString(httpCode).c_str());
}
https.end();
}
else
{
Serial.println("Unable to connect to make.com");
}
}
delete client;
}
else
{
Serial.println("Unable to create https client");
}
}

Integrate With Shortcuts and Siri

WhatsApp Image 2025-03-29 at 22.49.46.jpeg
WhatsApp Image 2025-03-29 at 22.51.58 (1).jpeg
WhatsApp Image 2025-03-29 at 22.51.58 (2).jpeg
WhatsApp Image 2025-03-29 at 22.51.58.jpeg
WhatsApp Image 2025-03-29 at 22.51.59.jpeg

In order to have the full experience, we recommend to create 4 new shortucts.

  1. Start Sit-ups
  2. Start Pushups
  3. Next Set
  4. Stop Workout

Each one can be controlled from siri and give you a feedback after the operation is finished.

Each operation is sent to blynk and updates the esp32 modes.


How to create a shortuct?

  1. In your Iphone, search for "Shortcuts" app
  2. Click on the + button in order to create your new shortcut
  3. Select "Add action"
  4. Search for "URL"
  5. Press the "URL" placeholder and paste the following URL: https://blynk.cloud/external/api/update?token={your_blynk_token}&V{insert_the_relevant_id_from_the_photo}={insert_the_relevant_id_from_the_photo}
  6. Congrats! you can start fitting up!


Integrate With Blynk

blynk datastream photo1.png
blynk datastream photo2.png
blynk datastream photo3.png
blynk website photo.png

We want to control and display the workout progress to the trainee, that's when Blynk will be useful.

Open Blynk website and create an account. Now head over to the Developer Zone section and create a new template, then add to this template a new device. Next open the Datastreams and add 7 new datastreams:

  1. name: Start sit ups workout, pin: V0, data type: Integer, min value: 0, max value: 1, default value: 0
  2. name: Start push ups workout, pin: V1, data type: Integer, min value: 0, max value: 1, default value: 0
  3. name: Number of push ups, pin: V2, data type: Integer, min value: 0, max value: 100000, default value: 0
  4. name: Number of sit ups, pin: V3, data type: Integer, min value: 0, max value: 100000, default value: 0
  5. name: Set, pin: V4, data type: Integer, min value: 0, max value: 100000, default value: 0
  6. name: Reset, pin: V5, data type: Integer, min value: 0, max value: 1, default value: 0
  7. name: Start new set, pin: V6, data type: Integer, min value: 0, max value: 1, default value: 0

Now head over to the Web Dashboard section and add 4 Switch widgets to the dashboard, one for each switch datastream (Start sit ups workout, Start push ups workout, Reset and Start new set), configure each switch widget to its corresponding datastream and name them accordingly (see attached photo).

Next add 3 Number Input widgets to the dashboard, one for each number input datastream (Number of push ups, Number of sit ups and Set), configure each number input widget to its corresponding datastream and name them accordingly (see attached photo).

Download the Blynk app on your phone and follow the same steps in order to create the same workout dashboard in your phone.

Integrate Blynk in the End-to-End workout code:

First define

// Blynk configurations
#define BLYNK_TEMPLATE_ID "your_blynk_template_id"
#define BLYNK_TEMPLATE_NAME "End to End Workout"
#define BLYNK_AUTH_TOKEN "your_blynk_auth_token"

// WiFi credentials
char ssid[] = "your_wifi_ssid";
char pass[] = "your_wifi_password";

// workout state variables
bool isPushUpsWorkoutStarted = false;
bool isSitUpsWorkoutStarted = false;
int lastSetCount = 1;

// set state variables
int numberOfPushUps = 0;
int numberOfSitUps = 0;

Add to your setup function this configuration.

note: both V5 (Reset workout) and V6 (Start new set) are hidden because the user controls them via speaking to Siri which is more convenient when working out, so there is no reason for them to be displayed in the Blynk dashboard.

// add this to your setup function
// connect to Blynk
Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass);

// configure all pins to initial state
Blynk.virtualWrite(V0, 0); // reset Start sit ups in Virtual Pin V0
Blynk.virtualWrite(V1, 0); // reset Start push ups in Virtual Pin V1
Blynk.virtualWrite(V2, 0); // reset push-ups count in Virtual Pin V2
Blynk.virtualWrite(V3, 0); // reset sit-ups count in Virtual Pin V3
Blynk.virtualWrite(V4, 1); // reset set number to 1 in Virtual Pin V4
Blynk.setProperty(V5, "isHidden", true); // hide Reset switch in the Blynk dashboard
Blynk.setProperty(V6, "isHidden", true); // hide Start new set switch in the Blynk dashboard

Next add BLYNK_WRITE(V0) and BLYNK_WRITE(V1) functions in order to handle the values of the switch widgets of Start Sit-ups Workout and Start Push-ups Workout

// Blynk functions
// handle Start sit ups workout switch widget
BLYNK_WRITE(V0)
{
// variable of pin V0 value
int pinValue = param.asInt();
// if switch widget is off reset Number of sit ups to 0
if (pinValue == 0)
{
isSitUpsWorkoutStarted = false;
Blynk.virtualWrite(V3, 0); // Number of sit ups pin V3
}
// if switch widget is on then start sit ups workout
else
{
isSitUpsWorkoutStarted = true;
Blynk.virtualWrite(V1, 0); // turn off Start push ups workout
}
numberOfSitUps = 0;
}


// handle Start push ups workout switch widget
BLYNK_WRITE(V1)
{
// variable of pin V1 value
int pinValue = param.asInt();
// if switch widget is off reset Number of push ups to 0
if (pinValue == 0)
{
isPushUpsWorkoutStarted = false;
Blynk.virtualWrite(V2, 0); // Number of sit ups pin V2
}
// if switch widget is on then start push ups workout
else
{
isPushUpsWorkoutStarted = true;
Blynk.virtualWrite(V0, 0); // turn off Start sit ups workout
}
numberOfPushUps = 0;
}

Then add BLYNK_WRITE(V5) and BLYNK_WRITE(V6) functions in order to handle the values of the switch widgets of Reset Workout and Start New Set

// handles Reset switch widget
BLYNK_WRITE(V5)
{
// variable of pin V5 value
int command = param.asInt();
// if the Reset switch is on then end the workout
if (command == 1)
{
// turn on water tank and the light in the shower at the end of the workout via control4
turnOnWaterTank();
turnOnLightInShower();
// send final set (of workout) summary to telegram bot
sendWorkoutDataToTelegramBot(numberOfPushUps, numberOfSitUps, lastSetCount);
Blynk.virtualWrite(V4, 1); // reset set count
Blynk.virtualWrite(V5, 0); // turn off reset switch
Blynk.virtualWrite(V0, 0); // turn off start sit-ups switch
Blynk.virtualWrite(V1, 0); // turn off start push-ups switch
Blynk.virtualWrite(V3, 0); // reset sit-ups count
Blynk.virtualWrite(V2, 0); // Reset push-ups count
// reset workout state variables
isPushUpsWorkoutStarted = false;
isSitUpsWorkoutStarted = false;
// reset workout set state variable
lastSetCount = 1;
}
}

the function BLYNK_WRITE(V6) controls of the set counting which is an integral part if this project

// handles Set switch widget to start a new set
BLYNK_WRITE(V6)
{
// variable of pin V6 value
int command = param.asInt();
// if the Set switch is on then start a new set in the workout
if (command == 1)
{
// send set summary to telegram bot
sendWorkoutDataToTelegramBot(numberOfPushUps, numberOfSitUps, lastSetCount);
lastSetCount++; // update the set count in the workout
Blynk.virtualWrite(V4, lastSetCount); // add set to the number input widget in Blynk
// reset set state variables
numberOfPushUps = 0;
numberOfSitUps = 0;
resetPins();
Blynk.virtualWrite(V6, 0); // turn off reset switch
}
}

// reset set state number input widgets values
void resetPins()
{
if (isPushUpsWorkoutStarted)
{
Blynk.virtualWrite(V2, 0); // reset push-ups count
}
else
{
Blynk.virtualWrite(V3, 0); // reset sit-ups count
}
}

Finally, don't forget to add this to your loop function

// add this to your loop function
Blynk.run();

Push-ups and Sit-ups Counting Using IMU

danielle.jpg

The main feature that is the core of the project is triggering the push-ups and sit-ups workout count.

We do it using an accelerator that checks if a push-ups or sit-ups set in the workout has happened.

In order to prevent spamming the blynk app, we added a check if we detcted a movment more than 3 times to be sure that it was actually moved. it helped us prevent wrong counting and be sure that the values are correct and works as expected.

How to intergate:

use the accelgyro library

#include "I2Cdev.h"
#include "MPU6050.h"

Also you should define those variables to make sure the usage of the accelgyro will work.

// IMU configuration
#define OUTPUT_READABLE_ACCELGYRO

MPU6050 accelgyro;

// IMU axis configuration
int16_t ax, ay, az;
int16_t gx, gy, gz;

// accelerator state variables
float yAxisAccelerator = 0;
int countDirectionMovement = 0;

// workout state variables
bool isPushUpsWorkoutStarted = false;
bool isSitUpsWorkoutStarted = false;
int numberOfPushUps = 0;
int numberOfSitUps = 0;
int lastSentNumberOfPushUps = 0;
int lastSentNumberOfSitUps = 0;

// workout send time interval variables
unsigned long lastPushUpsSendTime = 0;
unsigned long lastSitUpsSendTime = 0;
unsigned long sendInterval = 500;

Add this to your setup function

// add to setup function
accelgyro.initialize();

How we record push-ups:

// record number of push-ups in a single set
void recordPushUps()
{
// send data to Blynk at specified intervals
if (millis() - lastPushUpsSendTime > sendInterval && isPushUpsWorkoutStarted)
{
accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);

// detect if any movement recognized in y axis
if (ay < 0 && yAxisAccelerator > 0)
{
countDirectionMovement++;
}
// detect if any movement recognized in x axis
if (yAxisAccelerator < 0 && ay > 0) {
countDirectionMovement++;
}

// if there were 2 direction changes it is a push-up
if (countDirectionMovement >= 2)
{
numberOfPushUps++;
countDirectionMovement = 0;
}

// in order to make sure blynk is not spammed
if (numberOfPushUps != lastSentNumberOfPushUps)
{
Blynk.virtualWrite(V2, numberOfPushUps); // send the number of push-ups to blynk Virutal Pin called V2
lastSentNumberOfPushUps = numberOfPushUps;
}
yAxisAccelerator = ay;
lastPushUpsSendTime = millis();
}
}

How we record sit-ups:

// record number of sit-ups in a single set
void recordSitUps()
{
// send data to Blynk at specified intervals
if (millis() - lastSitUpsSendTime > sendInterval && isSitUpsWorkoutStarted)
{
accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);

// detect if any movement recognized in y axis
if (ay < 0 && yAxisAccelerator > 0)
{
countDirectionMovement++;
}
// detect if any movement recognized in y axis
if (yAxisAccelerator < 0 && ay > 0) {
countDirectionMovement++;
}

// if there were 3 direction changes it is a sit-up
if (countDirectionMovement >= 3)
{
numberOfSitUps++;
countDirectionMovement = 0;
}

// in order to make sure blynk is not spammed
if (numberOfSitUps != lastSentNumberOfSitUps)
{
Blynk.virtualWrite(V3, numberOfSitUps); // send the number of sit-ups to blynk Virutal Pin called V3
lastSentNumberOfSitUps = numberOfSitUps;
}
yAxisAccelerator = ay;
lastSitUpsSendTime = millis();
}
}

From both of the function, you can see that we send the number of sit-ups/push-ups to the blynk application to show the relevant data to the user.

Pay attention, in order to record sit-ups and push-ups you should integrate with blynk, see next step.

Finally, add this to your loop function

// add to loop function
recordPushUps();
recordSitUps();

Add a Signal Light

IMG_1006.JPG

In this step we will add a signal light that indicates whether the workout started, this way the trainee knows when to start the workout.

We will need a led bulb, in our case we used a blue led (as shown in the photo).

Connect the led to the esp32 as written in the Step 1. Now all that left to do is to write the code that will use the led:

First define

#define SIGNAL_LED_PIN 15 // signal led pin

Next you will need to add this to your setup function

pinMode(SIGNAL_LED_PIN, OUTPUT); // initialize signal led pin as an output

Now you need to edit BLYNK_WRITE(V0) and BLYNK_WRITE(V1) functions in order to control the signal led respectively to the workout state

// Blynk functions
// handle Start sit ups workout switch widget
BLYNK_WRITE(V0)
{
// variable of pin V0 value
int pinValue = param.asInt();
// if switch widget is off reset Number of sit ups to 0
if (pinValue == 0)
{
isSitUpsWorkoutStarted = false;
---> edit here
digitalWrite(SIGNAL_LED_PIN, LOW); // turn the LED off
<---
Blynk.virtualWrite(V3, 0); // Number of sit ups pin V3
}
// if switch widget is on then start sit ups workout
else
{
isSitUpsWorkoutStarted = true;
---> edit here
digitalWrite(SIGNAL_LED_PIN, HIGH); // turn the LED on
<---
Blynk.virtualWrite(V1, 0); // turn off Start push ups workout
}
numberOfSitUps = 0;
}

// handle Start push ups workout switch widget
BLYNK_WRITE(V1)
{
// variable of pin V1 value
int pinValue = param.asInt();
// if switch widget is off reset Number of push ups to 0
if (pinValue == 0)
{
isPushUpsWorkoutStarted = false;
---> edit here
digitalWrite(SIGNAL_LED_PIN, LOW); // turn the LED off
<---
Blynk.virtualWrite(V2, 0); // Number of sit ups pin V2
}
// if switch widget is on then start push ups workout
else
{
isPushUpsWorkoutStarted = true;
---> edit here
digitalWrite(SIGNAL_LED_PIN, HIGH); // turn the LED on
<---
Blynk.virtualWrite(V0, 0); // turn off Start sit ups workout
}
numberOfPushUps = 0;
}

In addition you need to edit BLYNK_WRITE(V5) function in order to turn off the led as the Reset Workout switch was on

// handles Reset switch widget
BLYNK_WRITE(V5)
{
// variable of pin V5 value
int command = param.asInt();
// if the Reset switch is on then end the workout
if (command == 1)
{
// turn on water tank and the light in the shower at the end of the workout via control4
turnOnWaterTank();
turnOnLightInShower();
// send final set (of workout) summary to telegram bot
sendWorkoutDataToTelegramBot(numberOfPushUps, numberOfSitUps, lastSetCount);
Blynk.virtualWrite(V4, 1); // reset set count
Blynk.virtualWrite(V5, 0); // turn off reset switch
Blynk.virtualWrite(V0, 0); // turn off start sit-ups switch
Blynk.virtualWrite(V1, 0); // turn off start push-ups switch
Blynk.virtualWrite(V3, 0); // reset sit-ups count
Blynk.virtualWrite(V2, 0); // Reset push-ups count
// reset workout state variables
isPushUpsWorkoutStarted = false;
isSitUpsWorkoutStarted = false;
// reset workout set state variable
lastSetCount = 1;
---> edit here
digitalWrite(SIGNAL_LED_PIN, LOW); // turn the LED off
<---
}
}

Add "Success" Lights and Sound

green_lior.JPG

In this step we will add lights and sound to indicate a successful push-up or sit-up. We find it helpful for the trainee to be aware to his workout state without having to glance to the Blynk screen every sit-up or push-up.

We will need a led strip and a speaker, in our case we used a led strip with 13 leds (as shown in the photo).

Connect the led strip and the speaker to the esp32 as written in the Step 1. Now all that left to do is to write the code that will use the led strip and the speaker:

First include the pitches.h file (attached below) in order to play sounds in the speaker and Adafruit modules for the led strip

#include "pitches.h"
// Adafruit modules
#include <Adafruit_NeoPixel.h>
#include <Adafruit_Sensor.h>

Next define

#define LED_STRIP_PIN 16 // led strip pin
#define LED_COUNT 13 // number of leds in the strip
#define SPEAKER_PIN 26 // speaker pin

// speaker configuration
const int SPEAKER_PWM_CHANNEL = 0;
int successMelodyNotes[] = {
NOTE_E7, NOTE_G7, NOTE_E7, NOTE_C7, NOTE_D7, NOTE_G6,
NOTE_C7, NOTE_E6, NOTE_A6, NOTE_B6, NOTE_A6, NOTE_GS6, NOTE_B6, NOTE_GS6
};

// configure Adafruit led strip
Adafruit_NeoPixel strip(LED_COUNT, LED_STRIP_PIN, NEO_GRB + NEO_KHZ800);

Next add this to your setup function

// add to your setup function
ledcAttachPin(SPEAKER_PIN, SPEAKER_PWM_CHANNEL); // initialize speaker pin

Now let's write the code for the success sequence (lights and sound):

// plays success lights and sound
void triggerSuccessSequence()
{
startNotesMelody(successMelodyNotes, 14); // play success melody
flashSuccessLights(); // flash success lights
}

// flash the green color when the trainee succeeded
void flashSuccessLights()
{
for (byte flashIndex = 0; flashIndex < 3; flashIndex++)
{
for (byte ledIndex = 0; ledIndex < LED_COUNT; ledIndex++)
{
strip.setPixelColor(ledIndex, strip.Color(0, 255, 0)); // green light
}
strip.show();
delay(150);
turnOffLeds();
delay(150);
}
}

// turns off the leds in the led strip
void turnOffLeds()
{
// turn off every led in the led strip
for (int ledsIndex = 0; ledsIndex < LED_COUNT; ledsIndex++)
{
strip.setPixelColor(ledsIndex, strip.Color(0, 0, 0));
}
strip.show();
}

// plays every note in the the given melody using pwm channel
void startNotesMelody(int *melody, int melodyLen)
{
for (int note = 0; note < melodyLen; note++)
{
int noteDuration = 50;
int notesDelay = noteDuration * 0.20;
ledcWriteTone(SPEAKER_PWM_CHANNEL, melody[note]);
delay(noteDuration);
ledcWrite(SPEAKER_PWM_CHANNEL, 0);
delay(notesDelay);
}
}

Finally, you need to edit recordSitUps() and recordPushUps() in order to play the success sequence (lights and success melody) every successful push-up or sit-up

// record number of sit-ups in a single set
void recordSitUps()
{
// send data to Blynk at specified intervals
if (millis() - lastSitUpsSendTime > sendInterval && isSitUpsWorkoutStarted)
{
accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);

// detect if any movement recognized in y axis
if (ay < 0 && yAxisAccelerator > 0)
{
countDirectionMovement++;
}
// detect if any movement recognized in y axis
if (yAxisAccelerator < 0 && ay > 0) {
countDirectionMovement++;
}

// if there were 3 direction changes it is a sit-up
if (countDirectionMovement >= 3)
{
numberOfSitUps++;
countDirectionMovement = 0;
}

// in order to make sure blynk is not spammed
if (numberOfSitUps != lastSentNumberOfSitUps)
{
Blynk.virtualWrite(V3, numberOfSitUps); // send the number of sit-ups to blynk Virutal Pin called V3
lastSentNumberOfSitUps = numberOfSitUps;
---> edit here
triggerSuccessSequence();
<---
}
yAxisAccelerator = ay;
lastSitUpsSendTime = millis();
}
}

// record number of push-ups in a single set
void recordPushUps()
{
// send data to Blynk at specified intervals
if (millis() - lastPushUpsSendTime > sendInterval && isPushUpsWorkoutStarted)
{
accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);

// detect if any movement recognized in y axis
if (ay < 0 && yAxisAccelerator > 0)
{
countDirectionMovement++;
}
// detect if any movement recognized in x axis
if (yAxisAccelerator < 0 && ay > 0) {
countDirectionMovement++;
}

// if there were 2 direction changes it is a push-up
if (countDirectionMovement >= 2)
{
numberOfPushUps++;
countDirectionMovement = 0;
}

// in order to make sure blynk is not spammed
if (numberOfPushUps != lastSentNumberOfPushUps)
{
Blynk.virtualWrite(V2, numberOfPushUps); // send the number of push-ups to blynk Virutal Pin called V2
lastSentNumberOfPushUps = numberOfPushUps;
---> edit here
triggerSuccessSequence();
<---
}
yAxisAccelerator = ay;
lastPushUpsSendTime = millis();
}
}

Downloads

Using End-To-End Workout

Now that all is done, and you are ready to go, you can select your desired workout:

  1. Push-ups
  2. Sit-ups

If Sit-ups has been chosen, put the strapped wrapping box on your belly.

If Push-ups has been chosen, put the strapped wrapping box on your back.

You can start each mode from blynk clicking on the relevant "start" buttons, or you can create the siri shortcuts and use them for hand free usage.

In order to know that the workout has been started, you can see the small blue led turning on, which signs that the workout can start.

In case you want to use the "Sets" feature, you can use it only through siri shortcuts, that's due to our understanding that the trainee won't want to stop the entire workout in order to start the next set, and it will be much easier to do so through siri, otherwise he can just start and stop the whole workout through the blynk app.

You will pay attention that after every success push-ups/sit-ups you will hear a voice and the strapped wrapping box will be shining for a friendly feedback about your success in your workout.

At the end, you can turn off the workout by pressing on the button again, or asking siri to "stop workout" and the blue led light will be turned off.

In addition, at the end of each set (or ending the entire workout) a summary of your workout will be sent to the telegram bot, giving you the full workout data.

Making your life easier, when ending the workout the water tank and bathroom light will be turned on using control4 system.


For more visual explanation, please watch the video attached in the explanation.


We wish you an amazing end-to-end workout, keep fitting up!