Human Following Robot Using Arduino and Ultrasonic Sensor

Submitted by Gourav Tak on

Working of Human Following Robot Using Arduino

In recent years, robotics has witnessed significant advancements, enabling the creation of intelligent machines that can interact with the environment. One exciting application of robotics is the development of human-following robots. These robots can track and follow a person autonomously, making them useful in various scenarios like assistance in crowded areas, navigation support, or even as companions. In this article, we will explore in detail how to build a human following robot using Arduino and three ultrasonic sensors, complete with circuit diagrams and working code. Also, check all the Arduino-based Robotics projects by following the link.

The working of a human following robot using Arduino code and three ultrasonic sensors is an interesting project. What makes this project particularly interesting is the use of not just one, but three ultrasonic sensors. This adds a new dimension to the experience, as we typically see humans following a robot built with one ultrasonic, two IR, and one servo motor.  This servo motor has no role in the operation and also adds unnecessary complications. So I removed this servo and the IR sensors and used 3 ultrasonic sensors. With ultrasonic sensors, you can measure distance and use that information to navigate and follow a human target. Here’s a general outline of the steps involved in creating such a robot.

 

 

Components Needed for Human Following Robot Using Arduino

  • Arduino UNO board ×1

  • Ultrasonic sensor ×3

  • L298N motor driver ×1

  • Robot chassis

  • BO motors ×2

  • Wheels ×2

  • Li-ion battery 3.7V ×2

  • Battery holder ×1

  • Breadboard

  • Ultrasonic sensor holder ×3

  • Switch and jumper wires

Human Following Robot Using Arduino Circuit Diagram

Here is the schematic diagram of a Human-following robot circuit.

Arduino Human Following Robot Circuit Diagram

This design incorporates three ultrasonic sensors, allowing distance measurements in three directions front, right, and left. These sensors are connected to the Arduino board through their respective digital pins. Additionally, the circuit includes two DC motors for movement, which are connected to an L298N motor driver module. The motor driver module is, in turn, connected to the Arduino board using its corresponding digital pins. To power the entire setup, two 3.7V li-ion cells are employed, which are connected to the motor driver module via a switch.

Overall, this circuit diagram showcases the essential components and connections necessary for the Human-following robot to operate effectively.

arduino human following robot circuit

Circuit Connection:

Arduino and HC-SR04 Ultrasonic Sensor Module:

HC-SR04 Ultrasonic sensor Module

  • Connect the VCC pin of each ultrasonic sensor to the 5V pin on the Arduino board.

  • Connect the GND pin of each ultrasonic sensor to the GND pin on the Arduino board.

  • Connect the trigger pin (TRIG) of each ultrasonic sensor to separate digital pins (2,4, and 6) on the Arduino board.

  • Connect the echo pin (ECHO) of each ultrasonic to separate digital pins (3,5, and 7) on the Arduino board.

Arduino and Motor Driver Module:

  • Connect the digital output pins of the Arduino (digital pins 8, 9, 10, and 11) to the appropriate input pins (IN1, IN2, IN3, and IN4) on the motor driver module.

  • Connect the ENA and ENB pins of the motor driver module to the onboard High state pin with the help of a female header.

  • Connect the OUT1, OUT2, OUT3, and OUT4 pins of the motor driver module to the appropriate terminals of the motors.

  • Connect the VCC (+5V) and GND pins of the motor driver module to the appropriate power (Vin) and ground (GND) connections on the Arduino.

Power Supply:

  • Connect the positive terminal of the power supply to the +12V input of the motor driver module.

  • Connect the negative terminal of the power supply to the GND pin of the motor driver module.

  • Connect the GND pin of the Arduino to the GND pin of the motor driver module.

Human Following Robot Using Arduino Code

Here is a simple 3 Ultrasonic sensor-based Human following robot using Arduino Uno code that you can use for your project.

Ultrsonic Sensors on Robot

This code reads the distances from three ultrasonic sensors (‘frontDistance’, ‘leftDistance’, and ‘rightDistance’). It then compares these distances to determine the sensor with the smallest distance. If the smallest distance is below the threshold, it moves the car accordingly using the appropriate motor control function (‘moveForward()’, ‘turnLeft()’, ‘turnRight()’). If none of the distances are below the threshold, it stops the motor using ‘stop()’.

In this section, we define the pin connections for the ultrasonic sensors and motor control. The S1Trig, S2Trig, and S3Trig, variables represent the trigger pins of the three ultrasonic sensors, while S1Echo, S2Echo, and S3Echo, represent their respective echo pins.

The LEFT_MOTOR_PIN1, LEFT_MOTOR_PIN2, RIGHT_MOTOR_PIN1, and RIGHT_MOTOR_PIN2 variables define the pins for controlling the motors.

The MAX_DISTANCE and MIN_DISTANCE_BACK variables set the thresholds for obstacle detection.

// Ultrasonic sensor pins
#define S1Trig 2
#define S2Trig 4
#define S3Trig 6
#define S1Echo 3
#define S2Echo 5
#define S3Echo 7
// Motor control pins
#define LEFT_MOTOR_PIN1 8
#define LEFT_MOTOR_PIN2 9
#define RIGHT_MOTOR_PIN1 10
#define RIGHT_MOTOR_PIN2 11
// Distance thresholds for obstacle detection
#define MAX_DISTANCE 40
#define MIN_DISTANCE_BACK 5

Make sure to adjust the values of ‘MIN_DISTANCE_BACK’ and ‘MAX_DISTANCE’ according to your specific requirements and the characteristics of your robot.

The suitable values for ‘MIN_DISTANCE_BACK’ and ‘MAX_DISTANCE’ depend on the specific requirements and characteristics of your human-following robot. You will need to consider factors such as the speed of your robot, the response time of the sensors, and the desired safety margin

Here are some general guidelines to help you choose suitable values.

MIN_DISTANCE_BACK’ This value represents the distance at which the car should come to a stop when an obstacle or hand is detected directly in front. It should be set to a distance that allows the car to back safely without colliding with the obstacle or hand. A typical value could be around 5-10 cm.

MAX_DISTANCE’ This value represents the maximum distance at which the car considers the path ahead to be clear and can continue moving forward. It should be set to a distance that provides enough room for the car to move without colliding with any obstacles or hands. If your hand and obstacles are going out of this range, the robot should be stop. A typical value could be around 30-50 cm.

These values are just suggestions, and you may need to adjust them based on the specific characteristics of your robot and the environment in which it operates.

These lines set the motor speed limits. ‘MAX_SPEED’ denotes the upper limit for motor speed, while ‘MIN_SPEED’ is a lower value used for a slight left bias. The speed values are typically within the range of 0 to 255, and can be adjusted to suit our specific requirements.

// Maximum and minimum motor speeds
#define MAX_SPEED 150
#define MIN_SPEED 75

The ‘setup()’ function is called once at the start of the program. In the setup() function, we set the motor control pins (LEFT_MOTOR_PIN1, LEFT_MOTOR_PIN2, RIGHT_MOTOR_PIN1, RIGHT_MOTOR_PIN2) as output pins using ‘pinMode()’ . We also set the trigger pins (S1Trig, S2Trig, S3Trig) of the ultrasonic sensors as output pins and the echo pins (S1Echo, S2Echo, S3Echo) as input pins. Lastly, we initialize the serial communication at a baud rate of 9600 for debugging purposes.

void setup() {
  // Set motor control pins as outputs
  pinMode(LEFT_MOTOR_PIN1, OUTPUT);
  pinMode(LEFT_MOTOR_PIN2, OUTPUT);
  pinMode(RIGHT_MOTOR_PIN1, OUTPUT);
  pinMode(RIGHT_MOTOR_PIN2, OUTPUT);
  //Set the Trig pins as output pins
  pinMode(S1Trig, OUTPUT);
  pinMode(S2Trig, OUTPUT);
  pinMode(S3Trig, OUTPUT);
  //Set the Echo pins as input pins
  pinMode(S1Echo, INPUT);
  pinMode(S2Echo, INPUT);
  pinMode(S3Echo, INPUT);
  // Initialize the serial communication for debugging
  Serial.begin(9600);
}

This block of code consists of three functions (‘sensorOne()’, ‘sensorTwo()’, ‘sensorThree()’) responsible for measuring the distance using ultrasonic sensors.

The ‘sensorOne()’ function measures the distance using the first ultrasonic sensor. It's important to note that the conversion of the pulse duration to distance is based on the assumption that the speed of sound is approximately 343 meters per second. Dividing by 29 and halving the result provides an approximate conversion from microseconds to centimeters.

The ‘sensorTwo()’ and ‘sensorThree()’ functions work similarly, but for the second and third ultrasonic sensors, respectively.

// Function to measure the distance using an ultrasonic sensor
int sensorOne() {
  //pulse output
  digitalWrite(S1Trig, LOW);
  delayMicroseconds(2);
  digitalWrite(S1Trig, HIGH);
  delayMicroseconds(10);
  digitalWrite(S1Trig, LOW);
  long t = pulseIn(S1Echo, HIGH);//Get the pulse
  int cm = t / 29 / 2; //Convert time to the distance
  return cm; // Return the values from the sensor
}
//Get the sensor values
int sensorTwo() {
  //pulse output
  digitalWrite(S2Trig, LOW);
  delayMicroseconds(2);
  digitalWrite(S2Trig, HIGH);
  delayMicroseconds(10);
  digitalWrite(S2Trig, LOW);
  long t = pulseIn(S2Echo, HIGH);//Get the pulse
  int cm = t / 29 / 2; //Convert time to the distance
  return cm; // Return the values from the sensor
}
//Get the sensor values
int sensorThree() {
  //pulse output
  digitalWrite(S3Trig, LOW);
  delayMicroseconds(2);
  digitalWrite(S3Trig, HIGH);
  delayMicroseconds(10);
  digitalWrite(S3Trig, LOW);
  long t = pulseIn(S3Echo, HIGH);//Get the pulse
  int cm = t / 29 / 2; //Convert time to the distance
  return cm; // Return the values from the sensor
}

In this section, the ‘loop()’ function begins by calling the ‘sensorOne()’, ‘sensorTwo()’, and ‘sensorThree()’ functions to measure the distances from the ultrasonic sensors. The distances are then stored in the variables ‘frontDistance’, ‘leftDistance’, and ‘rightDistance’.

Next, the code utilizes the ‘Serial’ object to print the distance values to the serial monitor for debugging and monitoring purposes.

void loop() {
  int frontDistance = sensorOne();
  int leftDistance = sensorTwo();
  int rightDistance = sensorThree();
  Serial.print("Front: ");
  Serial.print(frontDistance);
  Serial.print(" cm, Left: ");
  Serial.print(leftDistance);
  Serial.print(" cm, Right: ");
  Serial.print(rightDistance);
  Serial.println(" cm");

In this section of code condition checks if the front distance is less than a threshold value ‘MIN_DISTANCE_BACK’ that indicates a very low distance. If this condition is true, it means that the front distance is very low, and the robot should move backward to avoid a collision. In this case, the ‘moveBackward()’ function is called.

if (frontDistance < MIN_DISTANCE_BACK) {
    moveBackward();
    Serial.println("backward");

If the previous condition is false, this condition is checked. if the front distance is less than the left distance, less than the right distance, and less than the ‘MAX_DISTANCE’ threshold. If this condition is true, it means that the front distance is the smallest among the three distances, and it is also below the maximum distance threshold. In this case, the ‘moveForward()’ function is called to make the car move forward.

else if (frontDistance < leftDistance && frontDistance < rightDistance && frontDistance < MAX_DISTANCE) {
    moveForward();
    Serial.println("forward");

If the previous condition is false, this condition is checked. It verifies if the left distance is less than the right distance and less than the ‘MAX_DISTANCE’ threshold. This condition indicates that the left distance is the smallest among the three distances, and it is also below the minimum distance threshold. Therefore, the ‘turnLeft()’ function is called to make the car turn left.

else if (leftDistance < rightDistance && leftDistance < MAX_DISTANCE) {
    turnLeft();
    Serial.println("left");

If neither of the previous conditions is met, this condition is checked. It ensures that the right distance is less than the ‘MAX_DISTANCE’ threshold. This condition suggests that the right distance is the smallest among the three distances, and it is below the minimum distance threshold. The ‘turnRight()’ function is called to make the car turn right.

else if (rightDistance < MAX_DISTANCE) {
    turnRight();
    Serial.println("right");

If none of the previous conditions are true, it means that none of the distances satisfy the conditions for movement. Therefore, the ‘stop()’ function is called to stop the car.

 else {
    stop();
    Serial.println("stop");

In summary, the code checks the distances from the three ultrasonic sensors and determines the direction in which the car should move based on the 3 ultrasonic sensors with the smallest distance.

 

Important aspects of this Arduino-powered human-following robot project include:

  • Three-sensor setup for 360-degree human identification
  • Distance measurement and decision-making in real-time
  • Navigation that operates automatically without human assistance
  • Avoiding collisions and maintaining a safe following distance

 

 

Technical Summary and GitHub Repository 

Using three HC-SR04 ultrasonic sensors and an L298N motor driver for precise directional control, this Arduino project shows off the robot's ability to track itself. For simple replication and modification, the full source code, circuit schematics, and assembly guidelines are accessible in our GitHub repository. To download the Arduino code, view comprehensive wiring schematics, and participate in the open-source robotics community, visit our GitHub page.

Code Schematics Download Icon

 

Frequently Asked Questions

⇥ How does an Arduino-powered human-following robot operate?
Three ultrasonic sensors are used by the Arduino-powered human following robot to determine a person's distance and presence. After processing this data, the Arduino manages motors to follow the identified individual while keeping a safe distance.

⇥ Which motor driver is ideal for an Arduino human-following robot?
The most widely used motor driver for Arduino human-following robots is the L298N. Additionally, some builders use the L293D motor driver shield, which connects to the Arduino Uno directly. Both can supply enough current for small robot applications and manage 2-4 DC motors.

⇥ Is it possible to create a human-following robot without soldering?
Yes, you can use motor driver shields that connect straight to an Arduino, breadboards, and jumper wires to construct a human-following robot. For novices and prototyping, this method is ideal.

⇥ What uses do human-following robots have in the real world?
Shopping cart robots in malls, luggage-carrying robots in airports, security patrol robots, elderly care assistance robots, educational demonstration robots, and companion robots that behave like pets are a few examples of applications.

 

Conclusion

This human following robot using Arduino project and three ultrasonic sensors is an exciting and rewarding project that combines programming, electronics, and mechanics. With Arduino’s versatility and the availability of affordable components, creating your own human-following robot is within reach.

Human-following robots have a wide range of applications in various fields, such as retail stores, malls, and hotels, to provide personalized assistance to customers. Human-following robots can be employed in security and surveillance systems to track and monitor individuals in public spaces. They can be used in Entertainment and events, elderly care, guided tours, research and development, education and research, and personal robotics.

They are just a few examples of the applications of human-following robots. As technology advances and robotics continues to evolve, we can expect even more diverse and innovative applications in the future.

Explore Practical Projects Similar To Robots Using Arduino

Explore a range of hands-on robotics projects powered by Arduino, from line-following bots to obstacle-avoiding vehicles. These practical builds help you understand sensor integration, motor control, and real-world automation techniques. Ideal for beginners and hobbyists, these projects bring theory to life through interactive learning.

 Simple Light Following Robot using Arduino UNO

Simple Light Following Robot using Arduino UNO

Today, we are building a simple Arduino-based project: a light-following robot. This project is perfect for beginners, and we'll use LDR sensor modules to detect light and an MX1508 motor driver module for control. By building this simple light following robot you will learn the basics of robotics and how to use a microcontroller like Arduino to read sensor data and control motors.

Line Follower Robot using Arduino UNO: How to Build (Step-by-Step Guide)

Line Follower Robot using Arduino UNO: How to Build (Step-by-Step Guide)

This step-by-step guide will show you how to build a professional-grade line follower robot using Arduino UNO, with complete code explanations and troubleshooting tips. Perfect for beginners and intermediate makers alike, this project combines hardware interfacing, sensor calibration, and motor control fundamentals.

Have any question related to this Article?

5 Hidden Features of the Arduino UNO R3 You Probably Didn’t Know

Submitted by Vedhathiri on

The Arduino UNO R3 is often the first board people use to learn electronics and programming. Connect an LED, read a sensor, control a motor, upload a sketch, and it can seem like you’ve already discovered everything the board can do.
But there is much more going on underneath.
At the heart of the UNO R3 is the ATmega328P microcontroller. That small chip contains hardware features that many Arduino users never directly use, including an internal temperature sensor, brown-out detection, multiple sleep modes, a watchdog timer, and pin-change interrupts. These are capabilities of the microcontroller that the UNO R3 is built around.
Here are five of the most interesting ones you can actually explore on an Arduino UNO R3.

1. Your Arduino Can Measure Its Own Temperature

When you want to measure temperature with an Arduino, you would normally connect an external sensor such as an LM35, TMP36, DHT11, or DS18B20.
But the ATmega328P already has a temperature-sensing circuit inside the microcontroller.
The sensor is internally connected to the ADC, allowing the chip to obtain a temperature-related reading without using an external temperature sensor. The ATmega328P datasheet specifically lists temperature measurement among its peripheral features.

The basic path is:
Internal temperature sensor → ADC → digital reading → Serial Monitor

How to Demonstrate It

Connect your UNO R3 to your computer and configure the ATmega328P's ADC to read its internal temperature channel.
Then print the ADC result to the Serial Monitor.
For a visually interesting demonstration, start with the reading on screen and gently warm the microcontroller. You should see the reading change.
You don't need:

  • An LM35
  • A DHT11
  • A TMP36
    Any other external temperature sensor
    The sensing circuit is already inside the microcontroller.

One Important Catch

This is not a precision thermometer. The internal sensor has significant variation between individual chips, so the reading should be treated as approximate rather than as an accurate measurement of room temperature. So the safest way to describe it is:
“Your Arduino has a temperature sensor inside its main chip.”
Rather than:
“Your Arduino can accurately measure room temperature.”

Video Reference

How to Read the Internal Temperature Sensor in Arduino - Electro Hijibiji
https://www.youtube.com/watch?v=43l6Ibk7Spw

2. Your Arduino Can Detect When Its Supply Voltage Gets Too Low

Another capability that doesn't require an external sensor is Brown-Out Detection, or BOD.
The ATmega328P can monitor its supply voltage. When brown-out detection is enabled and the voltage falls below the selected threshold, the microcontroller can be held in reset rather than continuing to operate under an insufficient supply voltage. The chip also provides a brown-out reset flag that allows software to determine that a brown-out reset occurred.
Think of it as a built-in low-voltage protection mechanism for the microcontroller:
Normal supply

Supply voltage falls

Brown-out threshold

MCU reset/held in reset
This can be particularly useful in battery-powered embedded systems, where the supply voltage can change as the battery discharges.

How to Demonstrate It

For a controlled demonstration, use an appropriate adjustable power supply and a multimeter.
Gradually reduce the microcontroller's supply voltage while monitoring the system.
A simple visual sequence would be:
5 V → voltage decreases → threshold → reset

Safety

Don't experiment by randomly lowering the UNO's 5V rail while the board is simultaneously being powered through USB. Use a controlled setup and understand the board's power path before experimenting.

Video Reference

Arduino Project to Product – Part 4: Optimising Operating Voltage - Shawn Hymel / DigiKey
https://www.youtube.com/watch?v=7a4XYppZ6Bc

This is a useful engineering reference for ATmega328P operating voltage and brown-out detection.

3. Your Arduino Can Literally Go to Sleep

The ATmega328P doesn't have to keep its CPU fully active all the time.
It has six hardware sleep modes:

  • Idle
  • ADC Noise Reduction
  • Power-save
  • Power-down
  • Standby
  • Extended Standby

Each mode disables different parts of the chip to reduce power consumption.
The most interesting mode for low-power applications is Power-down.
Instead of keeping the microcontroller awake while it has nothing to do, you can design a system like this:

Do some work → sleep → wake → do some work → sleep again

This is extremely useful for battery-powered devices.
Imagine a sensor that only needs to collect data once every minute. There is little reason to keep the CPU fully active for the entire minute.

How to Demonstrate It

Use an Arduino UNO R3 with an LED and a wake-up source such as a push button.
Show:
Arduino running

Arduino enters sleep

Activity stops / current drops

Wake-up event

Arduino continues
For an even better demonstration, measure the current and show the difference between active and sleep states.

Why is this Useful?

Sleep modes are useful for:

  • Battery-powered sensors
  • Remote data loggers
  • Portable electronics
  • Environmental monitors
  • Low-power embedded systems

The basic idea is simple:
Don't spend power doing nothing.

Video Reference

Arduino Project to Product - Part 8: How to Put Arduino to Sleep - Shawn Hymel / DigiKey
https://www.youtube.com/watch?v=eQZf5pbEVxE

This is particularly useful because it demonstrates the relationship between sleep mode and power consumption.

4.  Your Arduino Can Restart Itself When Software Gets Stuck

This is one of the most useful features for real-world embedded projects.
The ATmega328P includes a hardware Watchdog Timer with a separate on-chip oscillator. The watchdog can be configured with a timeout, and if the software fails to service it before that timeout expires, it can trigger a system reset.
Imagine an unattended robot.
Everything is working:
Sensors → code → motors → communication
Then a software bug sends the program into an infinite loop.
Without a recovery mechanism, the system can remain frozen.
With the watchdog:
Program running

Software gets stuck

Watchdog isn't serviced

Timeout

Hardware reset

Program starts again
The watchdog doesn't actually understand that the program “crashed.” It simply detects that it wasn't serviced in time.

How to Demonstrate It

You can deliberately create an infinite loop after enabling the watchdog:

#include <avr/wdt.h>
void setup() {
Serial.begin(9600);
Serial.println("Arduino STARTED");
wdt_enable(WDTO_2S);
}
void loop() {
Serial.println("Running...");
delay(500);
// Simulate a software freeze
while (true) {
}
}

The Serial Monitor will initially show:
Arduino STARTED
Running...
Running...
Running...
Then the program stops responding.
After the watchdog timeout, the ATmega328P resets and setup() runs again.
You should see:
Arduino STARTED
again.
That makes a great demonstration because it looks like the Arduino has recovered itself.

Where is this Useful?

Watchdog timers are especially useful in:

  • Robotics
  • IoT devices
  • Remote sensors
  • Industrial controllers
  • Security systems
  • Unattended embedded systems

If something is supposed to keep running for hours, days, or months without someone nearby to press RESET, a watchdog can provide an important recovery mechanism.

Video Reference

Tutorial: Using the Arduino Watchdog Timer - MAKE Course
https://www.youtube.com/watch?v=BDsu8YhYn8g
The video specifically covers the watchdog timer on the ATmega328P and its automatic timeout/reset behavior.

5. Your Arduino Can Wake Up When a Configured Pin Changes

This is where the sleep feature becomes even more interesting.
The ATmega328P provides Pin Change Interrupts, commonly called PCINT.
On the UNO R3, pin-change interrupt sources are spread across groups of GPIO pins. For example, the ATmega328P uses PCINT groups corresponding to:
D8 - D13
A0 - A5
D0 - D7
Electronoobs' detailed ATmega328P tutorial explains these groups, the corresponding interrupt vectors, and how to configure them.
But the really useful part is this:
Pin-change interrupts can be used to wake the ATmega328P from Power-down sleep.
So you can create this sequence:
Arduino running

MCU enters sleep

Button changes a configured pin

Pin-change interrupt

Arduino wakes
The microcontroller doesn't have to keep executing a loop asking:
“Is the button pressed?”
while it is sleeping.
The hardware can detect the configured change and use the interrupt as a wake-up event.

How to Demonstrate It

For example, connect a push button like this:
A0 ───── BUTTON ───── GND
Configure A0 with the internal pull-up resistor and enable the appropriate pin-change interrupt.
Then show:
Arduino awake

Arduino enters sleep

Current/activity drops

Press button

A0 changes state

Pin-change interrupt

Arduino wakes

Why is this Different from Normal Button Reading?

Normally, your program might repeatedly poll the pin:
Is the button pressed?
Is the button pressed?
Is the button pressed?
That means the CPU has to remain active.
With the sleep + interrupt approach, the CPU can sleep until the hardware detects the configured event.

Video Reference

Pin Change Interruptions ISR | PCINT | Arduino101- Electronoobs
https://www.youtube.com/watch?v=ZDtRWmBMCmw

This video demonstrates pin-change interrupts on the Arduino/ATmega328P and explains the PCINT groups and configuration.

 

Have any question related to this Article?

We Tested JBD, Daly & JK BMS After the E-Rickshaw Hack — The Vulnerability Is in the Hardware

Submitted by Dharagesh on

We recently came across reports covered by news outlets about electric vehicles, specifically e-rickshaws, being remotely hacked and stalled in the middle of busy roads. The attackers were using Android apps, some of which were available on the Google Play Store under names like BAT-BMS, Epoch Li-on and Overkill Solar. These apps were apparently able to connect to some specific brands of the Battery Management System (BMS) inside these vehicles over Bluetooth and shut down the power MOSFETs, effectively cutting power to the motor while the vehicle was in motion.

The Indian government responded by removing several of these apps from the Play Store. That seemed like a reasonable first step, but it immediately raised a question for us: does removing the app actually solve the problem, or is the real issue deeper than that?

We suspected the latter. If a third-party app, one not even made by the BMS manufacturer, could gain control over the battery's safety switches, then the vulnerability isn't in the app. It's in the BMS itself. Removing one app from the store doesn't stop anyone from writing another one, or from sending the same Bluetooth commands directly from a laptop or microcontroller.

So we decided to test it ourselves. We purchased three of the most widely used BMS brands in the consumer and light-EV market JBD (Jiabaida), Daly, and JK (Jikong) and set out to determine whether their Bluetooth Low Energy (BLE) interfaces had any meaningful security against unauthorized access. But what we found was more shocking! All three BMS platforms permitted unauthenticated remote control of their safety-critical MOSFETs. An attacker within BLE range can scan, connect, read live battery telemetry, and toggle the charge and discharge FETs without pairing, bonding, password verification, or any other form of firmware-level authentication.

To demonstrate the impact of these findings, we also built a proof-of-concept web application to visually demonstrate this vulnerability, and developed a hardware countermeasure that existing BMS owners can deploy right now to protect their systems. This article covers our complete journey through this investigation.

A Brief Primer on BLE

Before we examine the vulnerabilities identified during this research, it is useful to understand the Bluetooth Low Energy (BLE) concepts that make them possible. While BLE is designed for low-power, short-range wireless communication, it also includes a well-defined data model and several built-in security mechanisms. Understanding these fundamentals provides the context needed to see why the BMS devices we tested are vulnerable.
Bluetooth Low Energy (BLE), introduced as part of Bluetooth 4.0, differs significantly from Classic Bluetooth. Rather than exposing a continuous serial data stream through the Serial Port Profile (SPP), BLE organises communication using the Generic Attribute Profile (GATT). Under the GATT model, a device exposes its functionality through a hierarchy of Services, each identified by a unique UUID (Universally Unique Identifier). Each service contains one or more Characteristics, also identified by UUIDs, which represent individual pieces of data or control interfaces. A characteristic consists of the actual value along with a set of properties that define how it may be accessed.

BLE Data Model Services and Characteristics

In practice, a Service represents a logical grouping of related functionality, such as battery telemetry or configuration settings. Within that service, Characteristics expose specific values such as pack voltage, cell temperatures, state of charge, or control commands. BLE identifies these objects using UUIDs, which are 128-bit identifiers typically represented in hexadecimal. Standard BLE services use shortened 16-bit UUIDs; for example, 0x180F identifies the standard Battery Service, whereas vendor-specific implementations generally use custom 128-bit UUIDs.
Each characteristic also advertises one or more access properties that determine how a connected client may interact with it. The Read property allows a client such as a phone or laptop to retrieve the current value stored by the device. The Write property enables the client to send data or commands to the device, making it the mechanism through which configuration changes or control operations are typically performed. Some devices additionally support Write Without Response, which omits the acknowledgement normally returned by the device, improving throughput at the cost of reliability. The Notify property allows the device to asynchronously push updates to subscribed clients without requiring repeated polling, making it the standard mechanism for streaming live telemetry such as voltage, current, and temperature measurements.

BLE Data Interaction How Devices Communicate

BLE also includes a comprehensive security architecture designed to prevent unauthorized access. Pairing establishes a trusted relationship between two devices using methods such as Passkey Entry, Numeric Comparison, or Out-of-Band authentication. Once pairing has been completed, Bonding allows the generated security keys to be stored so that future connections can be established securely without repeating the pairing process. These keys are then used to enable Encryption, protecting all subsequent communication over the BLE link. At the application layer, Authorization provides an additional level of access control by allowing the device to determine whether a connected client is permitted to perform sensitive operations, such as modifying configuration parameters or issuing control commands.
The most important observation for this research is that BLE already provides the mechanisms necessary to secure sensitive devices such as Battery Management Systems. The vulnerabilities described in the following sections do not arise from weaknesses in the BLE protocol itself; rather, they resulted from manufacturers choosing not to implement pairing, encryption, authorization, or other available security controls, leaving critical functionality accessible to any nearby BLE client.

Our Investigation

Testing the Official Apps

We began our investigation not with code or protocol analysers, but with the official manufacturer of mobile apps. Our goal was simple: before we try to bypass anything, let's see what security the manufacturers themselves claim to provide.

JBD - "JBD BMS" and "XiaoXiang Electric"

JBD offers two apps that behave similarly. When a user connects to a JBD BMS as a guest, they can view battery information but cannot access control functions. To gain full control, the user must register a JBD account and bind the BMS device ID to that account. The binding is stored on JBD's cloud servers, and the app checks the cloud database for ownership before granting control access.

JBD BMS App Interface

Here's the problem: if a BMS owner purchases the unit and does not register and bind it to their account, anyone else can create their own JBD account and bind that BMS to theirs, at which point they get full control through the official app itself. More importantly, even this account-binding mechanism is only enforced by the app. The BMS hardware itself, the JBD DP04S007 we tested, accepts control commands from any connected BLE device without authenticating anything. JBD does not provide any on-device password authentication option on the models we tested.

Daly - "DALY BMS"

Daly's official app does not require account creation. Any user can connect directly to the BMS. To access control functions like FET toggling, the app prompts for a password. The default password is `123456`, and users can change it. The password is stored on the BMS hardware itself.

Daly BMS App Interface

However, our testing on the Daly 4S12V60A confirmed something important: the BMS firmware accepts control commands over BLE regardless of whether a password has been sent in the session. The password prompt exists only in the app's user interface. It is a client-side gate. If you bypass the app and send the control command directly to the BLE characteristic, the BMS executes it without question.

JK - "JK BMS"

JK's app follows a similar pattern to Daly. Users can connect and set a password to protect their BMS, and the password is stored on the BMS hardware. But once again, direct BLE command injection bypasses this password entirely. The JK-B2A8S20P we tested processes write commands to its control characteristics without checking any authentication state. The password is only verified by the official app, not by the firmware.

JK BMS App Interface

Going Deeper: Protocol-Level Analysis

After confirming that all three brands had either no app-level security or trivially bypassable security, we needed to verify whether the underlying BLE protocol itself enforced any restrictions.

Our approach involved connecting to each BMS and enumerating all GATT services and characteristics to identify which ones are used for data exchange. We analysed the properties of each characteristic to determine which support Write (for sending control commands) and Notify (for receiving telemetry). To characterise the BLE interface, we performed protocol analysis using publicly available resources and controlled experimentation. Once reliable communication had been established, we verified normal telemetry functionality before assessing the control operations.

BLE Protocol Level Analysis

For responsible disclosure reasons, we are deliberately not publishing the specific BLE service and characteristic UUIDs, the command byte sequences, or the frame structures used by any of the three brands. What we can say is that all three follow a broadly similar pattern: the client writes a binary command frame to the BMS's writable characteristic, and the BMS processes it and sends a response back via notifications. The command frame contains a header marker, a target address byte, a command ID, a data payload, and a checksum. Telemetry commands return pack voltage, current, cell voltages, temperatures, SOC, and MOSFET states. Control commands toggle the charge and discharge MOSFETs.

The fundamental finding is simple: the BMS firmware does not distinguish between a legitimate user and an attacker. Any device that can establish a BLE connection and write the correct bytes to the correct characteristic can issue control commands.

Comparative Vulnerability Analysis

The following table summarises our findings across all three BMS brands side by side.

Security DimensionJBD (DP04S007)Daly (4S12V60A)JK (B2A8S20P)
BLE Pairing RequiredNoNoNo
BLE Bonding / EncryptionNoneNoneNone
App Password ProtectionAccount binding (cloud)Yes (user-configurable)

Yes (user-configurable)

Password Stored OnJBD cloud serversBMS hardwareBMS hardware
Password Enforced by BMS FirmwareNoNoNo
Direct BLE Command AcceptedYes - unrestrictedYes - unrestrictedYes - unrestricted
Telemetry Readable Without AuthYesYesYes
FET Control Without AuthYesYesYes
Third-Party App ControlYes (Epoch Li-ion, Overkill Solar)So far, none (Official Only)So far, none (Official Only)
Firmware-Level Authentication MechanismNone detectedNone detectedNone detected

The bottom line is that all three brands fail at the most fundamental level. The BMS firmware itself has no mechanism to verify that the device issuing control commands is authorised to do so. The "security" offered by their official apps is entirely cosmetic; it exists only in the app UI and can be bypassed by anyone who communicates directly with the BLE interface.

Our Test Setup

Our BMS Test Setup

All testing was conducted in a controlled laboratory environment using our own equipment. We used a 4-cell (4S) 18650 lithium-ion battery pack connected to an Electronic Load Measurement Tool for safe, controlled discharge testing. The BLE host was a Windows PC with a Bluetooth adapter running Python with the `bleak` BLE library. The three BMS units under test were the JBD DP04S007, the Daly 4S12V60A, and the JK-B2A8S20P. We verified MOSFET state changes through both BLE telemetry readback and physical measurement of load current on the electronic load.

Demonstrating the Vulnerability: The BMS Analyser App

To make this vulnerability tangible and visually demonstrable, we built a proof-of-concept web application called BMS Analyser. The app runs as a local web server on a PC with Bluetooth capability, and any device on the same WiFi network (a phone, tablet, or another laptop) can open its browser and interact with the BMS in real time. This approach lets us demonstrate the vulnerability to the viewer by seeing live battery data and interactive MOSFET toggle switches on their phone screen.

Architecture

The backend is written in Python using Flask. Since BLE operations are inherently asynchronous (using the bleak library), we run a dedicated asyncio event loop in a background thread. Flask's synchronous request handlers submit BLE coroutines to this loop and block until the result is available. This bridge pattern lets us keep the simplicity of Flask's REST API while still performing non-blocking BLE operations under the hood.

BMS Analyzer Architecture

Driver Abstraction

We designed the BLE communication layer around a clean driver abstraction. An abstract BMSDriver base class defines the interface: scan, connect, disconnect, get_telemetry, set_charge_fet, and set_discharge_fet and three concrete implementations, JBDBLEDriver, DalyBLEDriver and JKBLEDriver, encapsulate all the brand-specific protocol details. Each driver handles BLE service discovery, command frame construction with proper headers, addresses, payloads and checksums, notification response parsing, and the protocol quirks unique to each brand. For instance, Daly streams cell voltages across multiple notification frames that need to be reassembled by frame number, while JK sends 300-byte cell info responses that arrive in BLE MTU-sized chunks and must be accumulated before parsing.

The User Interface

The frontend is a single-page HTML/CSS/JavaScript application that polls the backend every 1.5 seconds for telemetry data. It presents a BLE scanner panel where you select a brand and scan for nearby devices, with signal strength indicators and a scrollable device list. Once connected, a live telemetry dashboard shows pack voltage, current, power, SOC percentage with an animated progress bar, individual cell voltages rendered as a bar chart, and temperature readings. Below that sits the MOSFET control panel: two toggle switches for the charge and discharge FETs, with colour-coded status cards that update in real time.

BMS Analyzer Setup Interface

One practical challenge sometimes in the UI is toggle switch glitching. Because telemetry is polled every 1.5 seconds, there's a race condition: when a user toggles a FET, the command takes 1-2 seconds to propagate to the BMS hardware. If a telemetry poll completes during that window, it reads the old MOSFET state and overwrites the switch position back, causing it to visually bounce between states. 

BMS Remote Access Control Demo

The Solution: BMS Protector

Discovering a vulnerability without proposing a solution is only half the job. So we developed a hardware-based security shield that existing BMS owners can deploy immediately, without any modifications to their BMS itself.

The Core Insight

BLE peripherals like these BMS units typically support only one active GATT connection at a time. This is a fundamental limitation of most BLE peripheral firmware stacks; once a client is connected, the device stops advertising and rejects additional connection attempts. We realised we could exploit this limitation defensively: if a trusted device permanently occupies the BMS's only BLE connection slot, no attacker can connect.

How It Works

The ESP32 BMS Protector is a small, inexpensive microcontroller. We used the Seeed Studio XIAO ESP32-S3 for its tiny form factor that connects to your BMS via BLE and holds the connection 24/7. By maintaining the active BLE GATT session link, the protector keeps the connection slot occupied. Because the BMS's single connection slot is now permanently occupied by the ESP32, any external device, whether it's an attacker's phone or a malicious app that tries to scan and connect, will either not see the BMS at all or get a connection-refused error.

BMS Shield Setup Interface

When you legitimately need to use your phone app or the BMS Analyser to check battery status, you can temporarily release the lock by pressing the ESP32's built-in BOOT button or by using its built-in Wi-Fi web portal. The protector disconnects from the BMS, giving you a configurable window (default: 5 minutes) to connect with your normal tools. Once the timer expires, the ESP32 automatically reconnects and re-locks the BMS.

On first power-up, the ESP32 has no saved configuration, so it enters CONFIG mode and creates a Wi-Fi access point called BMS-Shield-Setup. You connect your phone to this AP, open a browser, and a captive portal lets you select your BMS brand (JBD, Daly, or JK), scan for nearby BLE devices, and select your BMS from the list. The ESP32 saves this to non-volatile storage and reboots into LOCKED mode. From then on, it automatically connects to your BMS on every boot and holds the connection.

BMS Shield State Machine

The onboard LED communicates the current state visually: a double-blink pulse in CONFIG mode, solid ON when locked and connected, slow blink when attempting to reconnect after a dropped connection, and fast blink during the temporary release window. A long press (5+ seconds) on the BOOT button triggers a factory reset, clearing the saved configuration and returning to CONFIG mode.

Limitations and Recommendations

The proposed solution is a mitigation rather than a permanent fix. It requires additional hardware for each BMS unit, and consumes a small amount of standby power. Additionally, a determined attacker with specialised radio equipment could potentially jam the BLE connection to force a disconnect. While these limitations make the workaround practical for many applications, they highlight that the underlying vulnerability remains within the BMS firmware itself.
The long-term solution must come from BMS manufacturers through the implementation of proper BLE security. At a minimum, devices should support BLE pairing with passkey entry, requiring a unique numeric passkey during the initial connection to prevent unauthenticated access. BLE bonding should be enabled so that only previously paired devices can reconnect, and all BLE communication should be encrypted using AES-CCM to protect against eavesdropping and packet injection.
At the firmware level, the most critical improvement is robust command authentication. Before executing any safety-critical command such as MOSFET control, parameter modification, or factory reset, the firmware should verify that the connected device has successfully authenticated using a secure challenge-response mechanism rather than relying on a plaintext password transmitted over BLE. Permission levels should also be separated, allowing unauthenticated users to read battery telemetry while restricting control functions exclusively to authenticated devices.

GitHub Repository

Code and SchematicsDownload Zip File

Conclusion

The BLE security vulnerability we discovered across JBD, Daly, and JK BMS units is not an edge case or an obscure protocol weakness. It is a systemic design failure, a conscious decision (or oversight) by manufacturers to ship thousands of units with no authentication on safety-critical control functions. The technology to fix this has existed for over a decade in the BLE specification itself. Pairing, bonding, encryption, and authorisation are all well-defined, well-supported standards. The missing ingredient is not capability; it is priority.

We hope this research serves as a signal to the BMS industry that security cannot be an afterthought. Until manufacturers respond with proper firmware-level security, the ESP32 BMS Protector offers a practical, deployable defence for the thousands of vulnerable units already in the field.

Have any question related to this Article?