Build a desk buddy that reminds you to take breaks with Blynk

A printed desk buddy with ultrasonic eyes sees when you sit at your desk. After 45 minutes it turns red, chirps and sends a Blynk notification: time to stretch! The app shows your sitting time today.
  • Level:Advanced
  • Duration: 1 h
  • Ages 11+
  • Platform: ESP32

Tools for this lesson

Helpful for this build. Open a tool to see tips and more projects that use it.

Build a desk buddy that reminds you to take breaks with Blynk – EEMENTOR STEM™ lesson

Sitting still for hours is bad for your back and your concentration. This desk buddy keeps count for you. Its "eyes" are the ultrasonic sensor: they see when you sit in front of it. While you work, its green cheek glows. After 45 minutes it turns red, chirps and sends a notification to your phone. As soon as you have been away from the desk for two minutes, it counts that as a break and starts again. The Blynk app shows how long you have been sitting now and today, and a slider sets how often you want a break.

What you'll learn

  • How to use the ultrasonic sensor to tell whether someone is there
  • How to count time in steps with a BlynkTimer
  • How to decide what counts as a real break
  • How to set a value from a slider in the app and keep it after a restart

What you need

  • ESP32 board, expansion board and USB cable (ESP32 IoT Starter Kit)
  • Ultrasonic sensor HC-SR04 (ESP32 IoT Starter Kit)
  • 3 resistors, 10 kΩ, for the voltage divider (ESP32 IoT Starter Kit)
  • Green and red LEDs and 2 resistors, 220 Ω (ESP32 IoT Starter Kit)
  • Passive buzzer (ESP32 IoT Starter Kit)
  • A free Blynk account and the Blynk IoT app on a phone
  • A 2.4 GHz Wi-Fi network
  • The printed parts from the 3D printing box below (desk buddy), or the cardboard version described there
  • Breadboard and jumper wires (ESP32 IoT Starter Kit)

Wiring

Breadboard wiring diagram: Build a desk buddy that reminds you to take breaks with Blynk
Breadboard view. Check each connection against the wiring table below.
From To
Ultrasonic VCC 5V
Ultrasonic Trig ESP32 GPIO 26
Ultrasonic Echo 10 kΩ resistor, then ESP32 GPIO 27
ESP32 GPIO 27 Two 10 kΩ resistors in a row, then GND
Ultrasonic GND GND
ESP32 GPIO 25 220 Ω resistor, then the green LED's long leg
ESP32 GPIO 32 220 Ω resistor, then the red LED's long leg
Both LED short legs GND
Passive buzzer + ESP32 GPIO 33
Passive buzzer − GND

Protect the ESP32

The HC-SR04 runs on 5 V and answers with 5 V on its Echo pin, but the ESP32's pins are made for 3.3 V. Three equal 10 kΩ resistors fix that: one from Echo to the ESP32 pin, and two in a row from the ESP32 pin to GND. The pin then sees two thirds of 5 V, about 3.3 V. The radar capstone explains it in detail.

Set up Blynk

In the Blynk console, create a new template called Desk Buddy (hardware ESP32, connection type Wi-Fi) and add these datastreams. Lesson 1 of this path shows each step in detail.

Virtual pin Name Data type Range Phone widget
V0 Sitting now Integer 0–120 Gauge
V1 Today Integer 0–1000 Value display
V2 Break every Integer 15–90 Slider
V3 Status String – Label

Then open the template's Events & Notifications tab, add an event with the code take_a_break (name: Time for a break) and turn on push notifications for it. The event code must match the sketch exactly.

Save the template, create a new device from it and copy its three BLYNK_… lines. In the phone app, open the device, tap the wrench icon and add a Gauge (V0), a Value display (V1), a Slider (V2) and a Label (V3). Link each widget to its datastream.

V0 and V1 are in minutes. Set the slider's step to 5.

Example screen for: Build a desk buddy that reminds you to take breaks with Blynk
Example screen with sample values. Yours may look different.

The sketch

Replace the three BLYNK_… lines at the top with the ones from your device's Device info page, and add your Wi-Fi name and password (2.4 GHz only). The BLYNK_… lines must stay above #include <BlynkSimpleEsp32.h>.

Change TIME_ZONE to your own time zone. The example is for Central Europe; search for "POSIX time zone" and your city to find yours (for example EET-2EEST,M3.5.0/3,M10.5.0/4 for Estonia or GMT0BST,M3.5.0/1,M10.5.0 for the UK).

#define BLYNK_TEMPLATE_ID "YOUR_TEMPLATE_ID"
#define BLYNK_TEMPLATE_NAME "Desk Buddy"
#define BLYNK_AUTH_TOKEN "YOUR_AUTH_TOKEN"
#define BLYNK_PRINT Serial          // show connection messages in the Serial Monitor

#include <WiFi.h>
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h>
#include <time.h>

char ssid[] = "YOUR_WIFI_NAME";
char pass[] = "YOUR_WIFI_PASSWORD";
const char* TIME_ZONE = "CET-1CEST,M3.5.0,M10.5.0/3";   // your time zone

const int TRIG_PIN = 26, ECHO_PIN = 27;
const int GREEN_PIN = 25, RED_PIN = 32, BUZZER_PIN = 33;
const int AT_DESK_CM = 90;                 // closer than this: you are there
const int BREAK_SECONDS = 120;             // away this long counts as a break

BlynkTimer timer;
int breakEvery = 45;                       // minutes, set by the slider
long sitting = 0;                          // seconds since the last break
long todayTotal = 0;                       // seconds at the desk today
long away = 0;                             // seconds away from the desk
bool here = false, breakSent = false;
unsigned long lastChirp = 0;
int today = -1;

int measureCm() {
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);
  long us = pulseIn(ECHO_PIN, HIGH, 30000);
  return us == 0 ? 400 : min(us / 58, 400L);
}

void chirp() {
  tone(BUZZER_PIN, 1800, 100);
  timer.setTimeout(200L, []() { tone(BUZZER_PIN, 2400, 150); });
  lastChirp = millis();
}

void everyTwoSeconds() {
  int a = measureCm(), b = measureCm(), c = measureCm();
  int distance = max(min(a, b), min(max(a, b), c));   // median of three
  here = distance < AT_DESK_CM;

  if (here) {
    sitting += 2;
    todayTotal += 2;
    away = 0;
  } else {
    away += 2;
    if (away >= BREAK_SECONDS && sitting > 0) {      // a real break: start again
      sitting = 0;
      breakSent = false;
      Blynk.virtualWrite(V3, "On a break");
    }
  }

  bool due = sitting >= breakEvery * 60L;
  if (due && here && !breakSent) {
    Blynk.logEvent("take_a_break", String("You have been sitting for ") + breakEvery + " minutes. Time to stretch!");
    Blynk.virtualWrite(V3, "Time for a break!");
    breakSent = true;
    chirp();
  }
  if (due && here && millis() - lastChirp > 5UL * 60 * 1000) chirp();   // remind every 5 minutes
}

void lights() {                            // 4 times a second
  bool due = sitting >= breakEvery * 60L;
  digitalWrite(GREEN_PIN, here && !due);
  digitalWrite(RED_PIN, here && due && (millis() / 500) % 2);
}

void report() {                            // every 30 seconds
  Blynk.virtualWrite(V0, sitting / 60);
  Blynk.virtualWrite(V1, todayTotal / 60);
  if (here && sitting < breakEvery * 60L) Blynk.virtualWrite(V3, "Working");
  else if (!here && away < BREAK_SECONDS) Blynk.virtualWrite(V3, "Just stepped away");

  time_t now = time(nullptr);              // new day: start counting again
  if (now > 1700000000) {
    struct tm local;
    localtime_r(&now, &local);
    if (today != -1 && local.tm_yday != today) todayTotal = 0;
    today = local.tm_yday;
  }
}

BLYNK_WRITE(V2) {                          // Break every ... minutes
  breakEvery = constrain(param.asInt(), 15, 90);
}

BLYNK_CONNECTED() {
  Blynk.syncVirtual(V2);
}

void setup() {
  Serial.begin(115200);
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  pinMode(GREEN_PIN, OUTPUT);
  pinMode(RED_PIN, OUTPUT);
  Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass);
  configTzTime(TIME_ZONE, "pool.ntp.org");
  timer.setInterval(2000L, everyTwoSeconds);
  timer.setInterval(250L, lights);
  timer.setInterval(30000L, report);
}

void loop() {
  Blynk.run();
  timer.run();
}

Try it

  1. Set the slider to 15 minutes for a quick test, sit down and work. The green cheek glows.
  2. After 15 minutes the buddy turns red and blinks, chirps, and your phone shows "Time for a break".
  3. Stand up and leave the desk for two minutes. When you come back, the green cheek is on again and "Sitting now" has started from 0. Set the slider back to 45.

How it works

The ultrasonic sensor sends a short click of sound, too high to hear, and times the echo. Something closer than 90 cm, you, makes here true. Taking three readings and using the middle one, the median, ignores a single wrong echo.

The sketch doesn't need a clock to count minutes: everyTwoSeconds() runs every 2 seconds, so each call adds 2 to the seconds you have been sitting. Three timers do three jobs at their own speed: measuring every 2 s, the lights 4 times a second, and the app every 30 s.

Leaning back or fetching a pencil shouldn't count as a break. Only when you have been away for two minutes in a row does sitting go back to 0. The notification is sent once per sitting session, and the chirp repeats every five minutes until you get up.

Blynk.syncVirtual(V2) fetches the slider's value when the ESP32 connects, so your setting survives a restart.

Try this next

  • Count the breaks you took today and show them in the app.
  • Make the buddy remind you to drink: a blue light every hour.
  • Send a weekly summary: your average sitting time per day.

Troubleshooting

  • The device shows "Offline" in the app: check the Serial Monitor at 115200 baud. "Invalid auth token" means the BLYNK_AUTH_TOKEN line is wrong; if it never connects, check the Wi-Fi name and password, and that your network is 2.4 GHz.
  • It never sees you: the buddy points past you or you sit further than 90 cm. Turn it towards your chest, or raise AT_DESK_CM.
  • It sees you when you are gone: the chair back is in range. Lower AT_DESK_CM until an empty chair counts as "away".
  • The slider does nothing: it must be linked to V2, with a range of 15–90.
  • A printed part is too tight or too loose: printers differ by a few tenths of a millimetre. Sand or file a tight hole, and fix a loose part with a drop of hot glue or a strip of tape.

Build it yourself

Everything used in this lesson comes in one kit, with parts tested by our engineers.

ESP32 IoT Starter Kit | Arduino IDE Compatible | Free Tutorials $57.00

Shop the kit