DIY ESP32 pedometer

Walking is one of the easiest and healthiest forms of exercise. Whether you’re casually strolling, hiking, or trying to hit that 10,000-step daily goal, a pedometer is an excellent tool to track your steps. In this project, we will build a DIY ESP32 pedometer using an ESP32 microcontroller, a BMI160 sensor (accelerometer + gyroscope), a 0.96″ OLED display, and a 3.7V lithium-ion battery for portable operation.

This DIY project not only introduces you to the world of motion sensors and embedded systems but also results in a fully functional step counter that you can wear or carry around.

📋 ESP32 step counter project Materials Required

Component

Description

ESP32 Dev Board

Wi-Fi + Bluetooth MCU (e.g., ESP32 DevKitC, NodeMCU-ESP32)

BMI160 Sensor Module

6-axis accelerometer and gyroscope

0.96″ OLED Display

SSD1306 based, I2C communication

3.7V Lithium-Ion Battery

Rechargeable 18650 or Li-Po battery

TP4056 Charging Module

For safe battery charging (optional but recommended)

5V to 3.3V Regulator (optional)

If using a module without onboard regulator

Connecting Wires

Female-to-male jumper cables

Breadboard or PCB

For prototyping

Enclosure Case (optional)

To create a neat, wearable device

 

Note: The ESP32 step counter project also includes a boost converter, a slide switch for power management, and a push button that resets the step counter when pressed. Compact enough to fit in your pocket, this BMI160 pedometer records your walking steps accurately.

DIY ESP32 pedometer

🛠️ DIY ESP32 pedometer Circuit Diagram and Connections

The wiring is relatively simple because all the modules communicate via I2C protocol.

🔌 Connections:

ESP32 Pin

Component

Connection Description

3V3

OLED VCC, BMI160 VCC

Power for the modules

GND

OLED GND, BMI160 GND

Common ground

GPIO21 (SDA)

OLED SDA, BMI160 SDA

I2C Data line

GPIO22 (SCL)

OLED SCL, BMI160 SCL

I2C Clock line

 

Power Supply:

  • Connect the 3.7V battery to the VIN (or 5V pin) if your ESP32 board has an onboard 3.3V regulator.
  • Use a TP4056 module between your battery and ESP32 if you also want charging support.

 

⚙️ Setting Up the Environment

You’ll need the following software tools:

  • Arduino IDE (Latest Version)
  • ESP32 Board Manager installed in Arduino IDE
  • Adafruit SSD1306 library for the OLED
  • Adafruit GFX library for display graphics
  • BMI160 Library (You can install it via Arduino Library Manager or manually)

 

🧠 Understanding the Core Components of BMI160 pedometer

  1. ESP32

The ESP32 is a powerful microcontroller equipped with Wi-Fi, Bluetooth, dual-core processing, and sufficient GPIOs for connecting multiple peripherals.

  1. BMI160 Sensor

The BMI160 is a low-power, high-performance 6-axis IMU (Inertial Measurement Unit) sensor combining an accelerometer and a gyroscope. For our pedometer, we’ll primarily utilize the accelerometer readings.

  1. OLED Display

The 0.96″ OLED display will visually show the number of steps counted. It supports simple text and graphics with minimal power usage, perfect for battery-powered projects.

 

🧩 How the ESP32 pedometer Works

  • The BMI160 measures the acceleration across the three axes (X, Y, Z).
  • Each time your body moves, it creates a rhythmic pattern in acceleration.
  • We detect these changes and infer steps using a threshold-based method.
  • The step count is then displayed on the OLED screen in real-time.

📜 Arduino Sketch

Here’s a basic version of the code to get you started:

#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>
#include <BMI160Gen.h>

// OLED setup
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

// Variables
int stepCount = 0;
bool stepDetected = false;
float threshold = 1.2; // Step detection threshold

void setup() {
Serial.begin(115200);

// Initialize OLED
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F(“OLED allocation failed”));
while(true);
}
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);

// Initialize BMI160
if (BMI160.begin(BMI160GenClass::I2C_MODE, 0x68) < 0) {
Serial.println(“BMI160 initialization failed”);
while(1);
}

display.println(“Pedometer”);
display.display();
delay(2000);
display.clearDisplay();
}

void loop() {
int ax, ay, az;
BMI160.readAccelerometer(ax, ay, az);

float acceleration = sqrt(ax * ax + ay * ay + az * az) / 16384.0;

if(acceleration > threshold && !stepDetected) {
stepCount++;
stepDetected = true;
}
if(acceleration < threshold) {
stepDetected = false;
}

display.clearDisplay();
display.setCursor(0,10);
display.print(“Steps:”);
display.setCursor(0, 30);
display.print(stepCount);
display.display();

delay(100);
}

 

🧪 Wearable step tracker Testing and Calibration

  1. Upload the code using Arduino IDE.
  2. Power the ESP32 through the 3.7V lithium-ion battery.
  3. Walk or shake the device gently.
  4. Watch the step count increase on the OLED screen.

 

🔧 Fine-tuning:

  • Adjust the threshold value based on your walking style.
  • You might need to introduce a debounce timer to avoid double-counting steps.

 

🔋 Power Management Tips for wearable step tracker

Since the device is battery-powered, here are ways to maximize battery life:

  • Use deep sleep modes in ESP32 when idle.
  • Lower OLED brightness or update the display less frequently.
  • Optimize sensor polling rate — reading accelerometer less often saves power.
  • Use high-efficiency switching regulators instead of linear ones.

 

📦 Enclosure and Wearable Ideas

  • 3D print a custom lightweight enclosure.
  • Attach a belt clip or wristband.
  • Use a small power button to conserve energy when not in use.
  • Keep battery accessible for easy recharging via the TP4056 module.

 

🚀 Possible BMI160 pedometer Upgrades

  • Bluetooth Data Sync: Send step count to your phone using ESP32’s Bluetooth.
  • Calories Burned Calculator: Estimate calories based on step count and stride length.
  • Data Logger: Save daily steps to SPIFFS or an SD card.
  • Rechargeable Solar Panel: Attach a small solar charger for extended operation.

 

🌟 Quick Summary:

  • ESP32 handles all processing and display.
  • BMI160 detects motion (steps).
  • OLED screen shows the number of steps.
  • 3.7V Li-ion battery powers everything, making it portable.

 

🛠 Schematic Connections Table

ESP32 Pin

Connected to

Description

3V3

OLED VCC, BMI160 VCC

Power supply (3.3V)

GND

OLED GND, BMI160 GND

Common ground

GPIO21 (I2C SDA)

OLED SDA, BMI160 SDA

I2C data line

GPIO22 (I2C SCL)

OLED SCL, BMI160 SCL

I2C clock line

VIN (or 5V)

3.7V Battery (via TP4056 if used)

Main battery input

 

🖍 Visual Description (Text-based)

            +-----------------------------------------+
            |                ESP32                    |
            | +3V3 o------------------+                |
            |                         |                |
            | GND o---------+          |                |
            |              |           |                |
            | GPIO21 (SDA) o|-----------+--- OLED SDA    |
            |              |           +--- BMI160 SDA  |
            | GPIO22 (SCL) o|-----------+--- OLED SCL    |
            |              |           +--- BMI160 SCL  |
            |              |                           |
            |              +------> OLED VCC (to 3.3V) |
            |              +------> BMI160 VCC (to 3.3V)|
            |                                         |
            | VIN/5V o-----> 3.7V Li-ion Battery (via TP4056) |
            +-----------------------------------------+

 

🛡 Important Notes for this wearable step tracker:

  • Both the OLED and BMI160 are I2C devices, so they share the same SDA and SCL lines but must have different I2C addresses.
    (Usually, OLED is 0x3C, and BMI160 defaults to 0x68).
  • Make sure you are powering the BMI160 and OLED with 3.3V, NOT 5V, because the ESP32 and peripherals are 3.3V tolerant.
  • If you are using a TP4056 charging module, connect it between the battery and ESP32 to allow safe charging without disconnection.

 

🔋 Battery and Charging System (Optional)

Here’s a quick simple wiring if you add TP4056:

3.7V Lithium Battery
    |
    |-- TP4056 IN+
    |-- TP4056 IN-
 
TP4056 OUT+
    --> ESP32 VIN/5V

TP4056 OUT-
    --> ESP32 GND

This way, you can recharge the battery via a micro-USB connected to the TP4056 while still operating the ESP32!

 

📷 Simple Diagram (Quick Visual Reference)

3.7V Battery --> TP4056 --> ESP32 VIN/GND
                         |
          +--------------+
          |
         (3V3 Output)
            |
    +-------+--------+
    |                |
OLED VCC        BMI160 VCC
OLED GND        BMI160 GND
OLED SDA        BMI160 SDA --> ESP32 GPIO21 (SDA)
OLED SCL        BMI160 SCL --> ESP32 GPIO22 (SCL)

 

Add the following libraries to the Arduino library folder, before working on the coding and debugging part.

  1. BMI160 Library: https://github.com/hanyazou/BMI160-Arduino
  2. Adafruit GFX Library: https://github.com/adafruit/Adafruit-GFX-Library
  3. Adafruit SSD1306 Library: https://github.com/adafruit/Adafruit_SSD1306

DIY ESP32 pedometer

Send Pedometer (Steps Counts) Data over ESP32 BLE

To enhance this project with wireless functionality, you can take advantage of the ESP32’s built-in Bluetooth Low Energy (BLE) feature. You need to modify the original code by adding the necessary BLE libraries and creating a dedicated service and characteristic for the step count data. With these changes, the pedometer will broadcast the current step count to any BLE-enabled device, such as a smartphone or tablet.

Copy the following code and upload it to the ESP32 Board

#include <Wire.h>
#include <BMI160Gen.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
 
// OLED display definitions for a 0.96″ (128×64) display
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET   -1    // No dedicated reset pin
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
 
// Sensor parameters:
const int BMI160_I2C_ADDRESS = 0x68;  // BMI160 I2C Address
const int INTERRUPT_PIN      = 5;     // Digital pin used for sensor interrupt
 
// Reset button pin (connected to GP23)
const int RESET_BUTTON_PIN   = 23;    // When pressed, this pin is pulled HIGH
 
// BLE service and characteristic UUIDs (custom values)
#define SERVICE_UUID        “4fafc201-1fb5-459e-8fcc-c5c9c331914b”
#define CHARACTERISTIC_UUID “beb5483e-36e1-4688-b7f5-ea07361b26a8”
 
// BLE characteristic pointer and connection flag
BLECharacteristic *pCharacteristic;
bool deviceConnected = false;
 
// BLE server callbacks to print connection messages
class MyServerCallbacks: public BLEServerCallbacks {
  void onConnect(BLEServer* pServer) {
    deviceConnected = true;
    Serial.println(“BLE client connected”);
  }
  void onDisconnect(BLEServer* pServer) {
    deviceConnected = false;
    Serial.println(“BLE client disconnected”);
  }
};
 
// Optional: Interrupt callback (for future embellishments)
void stepInterrupt() {
  // This callback is triggered on a sensor interrupt.
  // The built-in step counter operates independently.
}
 
void setup() {
  Serial.begin(115200);
  while (!Serial) { } // Wait for Serial Monitor
 
  // Configure the reset button pin with internal pull-down
  pinMode(RESET_BUTTON_PIN, INPUT_PULLDOWN);
 
  // Initialize the OLED display
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {  // OLED I2C address is typically 0x3C
    Serial.println(F(“SSD1306 allocation failed”));
    while (1);
  }
  display.clearDisplay();
  display.display();
  
  // Initialize BMI160 sensor in I2C mode
  if (!BMI160.begin(BMI160GenClass::I2C_MODE, BMI160_I2C_ADDRESS, INTERRUPT_PIN)) {
    Serial.println(“BMI160 initialization failed!”);
    while (1);
  }
  BMI160.attachInterrupt(stepInterrupt);
  
  // — Auto-Calibration for Improved Accuracy —
  // Ensure the sensor is stationary during calibration!
  BMI160.autoCalibrateGyroOffset();
  BMI160.autoCalibrateXAccelOffset(0); // Calibrate X-axis to 0g
  BMI160.autoCalibrateYAccelOffset(0); // Calibrate Y-axis to 0g
  BMI160.autoCalibrateZAccelOffset(1); // Calibrate Z-axis to +1g (gravity)
  
  // Set accelerometer range to 2g for higher sensitivity in step detection
  BMI160.setFullScaleAccelRange(BMI160_ACCEL_RANGE_2G);
  
  // — Configure the Built-In Step Counter —
  // Options: BMI160_STEP_MODE_NORMAL, SENSITIVE, or ROBUST
  BMI160.setStepDetectionMode(BMI160_STEP_MODE_NORMAL);
  BMI160.setStepCountEnabled(true);
  BMI160.resetStepCount();
  
  // — Initialize BLE —
  BLEDevice::init(“ESP32_StepCounter”);
  BLEServer *pServer = BLEDevice::createServer();
  pServer>setCallbacks(new MyServerCallbacks());
  BLEService *pService = pServer>createService(SERVICE_UUID);
  pCharacteristic = pService>createCharacteristic(
                       CHARACTERISTIC_UUID,
                       BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY
                     );
  pCharacteristic>setValue(“0”);
  pService>start();
  BLEAdvertising *pAdvertising = pServer>getAdvertising();
  pAdvertising>start();
  Serial.println(“BLE advertising started”);
  
  // Initial OLED decoration
  display.clearDisplay();
  display.drawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SSD1306_WHITE);
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  String header = “BMI160 STEP COUNTER”;
  int headerWidth = header.length() * 6; // Approximate width at size 1
  int headerX = (SCREEN_WIDTH headerWidth) / 2;
  display.setCursor(headerX, 3);
  display.println(header);
  display.drawLine(0, 15, SCREEN_WIDTH, 15, SSD1306_WHITE);
  display.display();
  delay(1000);
}
 
void loop() {
  // Check if the reset button is pressed (reads HIGH when pressed)
  if (digitalRead(RESET_BUTTON_PIN) == HIGH) {
    BMI160.resetStepCount();
    Serial.println(“Counter reset!”);
    delay(300);  // Simple debounce delay
  }
  
  // Read the step count from the BMI160 sensor
  uint16_t currentSteps = BMI160.getStepCount();
  
  // Print the current step count to Serial Monitor
  Serial.print(“Steps: “);
  Serial.println(currentSteps);
  
  // Update OLED display
  display.clearDisplay();
  display.drawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SSD1306_WHITE);
  
  // — Header Section —
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  String header = “BMI160 STEP COUNTER”;
  int headerWidth = header.length() * 6;
  int headerX = (SCREEN_WIDTH headerWidth) / 2;
  display.setCursor(headerX, 3);
  display.println(header);
  display.drawLine(0, 15, SCREEN_WIDTH, 15, SSD1306_WHITE);
  
  // — Step Count Section —
  String stepText = String(currentSteps);
  display.setTextSize(3);
  int stepWidth = stepText.length() * 18; // Approximate width at size 3
  int stepX = (SCREEN_WIDTH stepWidth) / 2;
  display.setCursor(stepX, 25);
  display.println(stepText);
  
  // — Footer Section —
  display.setTextSize(1);
  String footer = “Steps”;
  int footerWidth = footer.length() * 6;
  int footerX = (SCREEN_WIDTH footerWidth) / 2;
  display.setCursor(footerX, 55);
  display.println(footer);
  
  display.display();
  
  // Update BLE characteristic and send notification if connected
  String stepsStr = String(currentSteps);
  pCharacteristic>setValue(stepsStr.c_str());
  pCharacteristic>notify();
  
  // Print BLE notification info to Serial Monitor if connected
  if (deviceConnected) {
    Serial.println(“BLE notification sent”);
  }
  
  delay(200); // Refresh display and BLE update every 200 ms
}

 

📝 Conclusion

Building your own DIY pedometer using an ESP32, BMI160 sensor, and OLED display is a rewarding project that blends hardware interfacing, sensor data processing, and low-power system design. Not only does it give you a better understanding of motion tracking and microcontroller programming, but you also end up with a practical, usable fitness tracker that you built yourself!

By carefully tuning the sensor thresholds and optimizing the power consumption, you can create a reliable, long-lasting DIY ESP32 pedometer ideal for personal use or even as a base for more advanced IoT fitness projects.

 

FAQ:
  • How long does the battery last, and can I optimize power consumption?

Battery life depends on usage and capacity (e.g., a 1000mAh LiPo may last ~24 hours). To extend it:

  1. Enable the ESP32’s deep sleep mode between step detections.
  2. Reduce the OLED display’s brightness/update frequency.
  3. Lower the BMI160’s sampling rate (e.g., 25Hz instead of 100Hz).
  4. Use a low-power linear regulator instead of a switching one.
  • Can I connect the pedometer to my smartphone via Bluetooth?

Yes, you can connect the DIY ESP32 pedometer to any BLE-enabled smartphone by utilizing the ESP32’s built-in Bluetooth Low Energy (BLE) capabilities. By modifying the code to create a BLE service and characteristic for the step data, the pedometer can broadcast the current step count, allowing you to view or log your activity using a compatible mobile app or a custom app developed specifically for this purpose.

  • How accurate is the DIY pedometer using the ESP32 and BMI160 sensor?

The accuracy of the DIY pedometer mainly depends on the calibration of the step detection algorithm and the threshold settings for the accelerometer data. While it may not match the precision of commercial wearable step tracker, it can achieve fairly accurate step counting with proper tuning. Adjusting the sensitivity and incorporating basic filtering can significantly improve the accuracy for casual walking or running activities.

 

Related:

IoT Heart Rate Monitoring with Arduino UNO R4 WiFi