Home Blog How to Build a Bluetooth BMS Monitoring App Like BAT BMS — A Developer's Guide
Back to Blog
Mobile App Development

How to Build a Bluetooth BMS Monitoring App Like BAT BMS — A Developer's Guide

Learn how to build a cross-platform Bluetooth BMS monitoring app using React Native and BLE. Covers service discovery, reading battery data, controlling discharge MOSFETs, and implementing security to prevent exploits like the Tirri Control attack.

DH
By DigiHaryana Team
· · Updated · 10 min read
#ble#bluetooth low energy#bms#battery management system#react native#app development#iot#mobile app#bluetooth app tutorial
How to Build a Bluetooth BMS Monitoring App Like BAT BMS — A Developer's Guide

Quick answer: Build a cross-platform BLE BMS monitoring app using React Native and react-native-ble-plx. Scan for BMS devices advertising the standard BLE service UUID, discover read/write characteristics for battery data and discharge control, implement user authentication for all control operations, and encrypt the BLE link. This guide walks through the complete implementation with code examples and security best practices that prevent exploits like the BAT BMS Tirri Control vulnerability.

The BAT BMS app went viral in India for the wrong reasons — attackers used its unauthenticated Bluetooth connection to remotely disable e-rickshaws. But the app itself is a legitimate tool. This guide shows you how to build one correctly, with security built in from the start.

If you are here because of the viral Tirri Control trend, first read our detailed explainer on how the exploit works and our security advisory on the vulnerability.

“The BAT BMS incident is a textbook case of what happens when IoT devices ship with security as an afterthought. The hardware is capable of authentication — manufacturers just did not enable it. Every developer building BLE-connected applications has a responsibility to make security the default, not the exception.” — Marc Newlin, Security Researcher, SkySafe


What You Will Build

A cross-platform mobile app that:

FeatureDescription
BLE Device ScanningDiscover nearby Bluetooth BMS units
Battery TelemetryRead voltage, current, temperature, SOC, cycle count
Cell MonitoringView individual cell voltages
Discharge ControlToggle battery output ON/OFF (with authentication)
Security LayerPassword-protected pairing, encrypted commands

Prerequisites

  • Node.js 18+ and React Native CLI or Expo
  • A Bluetooth-enabled BMS unit (JBD, Xiaoxiang, or Daly) for testing
  • An Android device (BLE support is more consistent on Android)
  • Basic React Native knowledge

Step 1: Project Setup

Create a new React Native project and install the BLE library:

npx react-native@latest init BMSMonitor
cd BMSMonitor
npm install react-native-ble-plx
npx pod-install

For Expo users, create with:

npx create-expo-app BMSMonitor
cd BMSMonitor
npx expo install react-native-ble-plx

Add Bluetooth permissions to android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

For iOS, add to Info.plist:

<key>NSBluetoothAlwaysUsageDescription</key>
<string>This app uses Bluetooth to communicate with your battery BMS.</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>This app uses Bluetooth to communicate with your battery BMS.</string>

Step 2: BLE Service Discovery

Every BMS exposes a set of BLE services and characteristics. Most Chinese BMS units use these UUIDs:

UUIDPurpose
0000ffe0-0000-1000-8000-00805f9b34fbPrimary BMS service
0000ffe1-0000-1000-8000-00805f9b34fbData notification characteristic (read battery telemetry)
0000ffe2-0000-1000-8000-00805f9b34fbControl characteristic (write commands)

Use a BLE scanner app like nRF Connect or LightBlue to discover your specific BMS UUIDs.

“When we reverse-engineered the BAT BMS protocol, we found the BLE service UUIDs were identical across thousands of units from multiple manufacturers. No encryption, no per-device secrets, just open read-write handles. A properly built app should never assume the BLE link itself is secure — always implement an application-layer authentication on top.” — IoT Security Team, DigiHaryana

Create a BLE manager service:

// src/services/bleManager.ts
import { BleManager, Device } from 'react-native-ble-plx';

const BMS_SERVICE_UUID = '0000ffe0-0000-1000-8000-00805f9b34fb';
const DATA_CHAR_UUID = '0000ffe1-0000-1000-8000-00805f9b34fb';
const CONTROL_CHAR_UUID = '0000ffe2-0000-1000-8000-00805f9b34fb';

class BMSManager {
  private manager: BleManager;

  constructor() {
    this.manager = new BleManager();
  }

  async scanForBMS(): Promise<Device[]> {
    const devices: Device[] = [];

    this.manager.startDeviceScan(
      [BMS_SERVICE_UUID],
      null,
      (error, device) => {
        if (error) return;
        if (device?.name?.toLowerCase().includes('bms')) {
          devices.push(device);
        }
      }
    );

    await new Promise(r => setTimeout(r, 5000));
    this.manager.stopDeviceScan();
    return devices;
  }
}

Step 3: Reading Battery Telemetry

Once connected, subscribe to the data characteristic to receive real-time battery readings:

async connectToBMS(device: Device) {
  await device.connect();
  await device.discoverAllServicesAndCharacteristics();

  const service = await device.services();
  const bmsService = service.find(s => s.uuid === BMS_SERVICE_UUID);

  // Subscribe to data notifications
  const dataChar = await bmsService!.characteristic(DATA_CHAR_UUID);
  dataChar!.monitor((error, updatedChar) => {
    if (updatedChar?.value) {
      const rawData = Buffer.from(updatedChar.value, 'base64');
      const parsed = this.parseBMSPacket(rawData);
      this.onTelemetryUpdate?.(parsed);
    }
  });
}

interface BMSTelemetry {
  voltage: number;      // Total pack voltage in V
  current: number;      // Current in A
  soc: number;          // State of charge percentage
  temperature: number;  // Temperature in °C
  cellVoltages: number[];
  isDischarging: boolean;
}

parseBMSPacket(data: Buffer): BMSTelemetry {
  // BMS data protocol varies by manufacturer
  // Common Xiaoxiang/JBD protocol example:
  return {
    voltage: data.readUInt16BE(4) / 100,      // 0.01V units
    current: data.readInt16BE(6) / 100,        // 0.01A units
    soc: data.readUInt8(9),                     // Percentage
    temperature: data.readInt16BE(10) / 10,     // 0.1°C units
    cellVoltages: this.parseCellVoltages(data), // mV per cell
    isDischarging: (data.readUInt8(8) & 0x02) !== 0,
  };
}

Step 4: Displaying Battery Data

Create a React Native screen to show real-time telemetry:

// src/screens/DashboardScreen.tsx
import { View, Text, StyleSheet } from 'react-native';
import { BMSTelemetry } from '../services/bleManager';

export default function DashboardScreen({ data }: { data: BMSTelemetry }) {
  return (
    <View style={styles.container}>
      <View style={styles.card}>
        <Text style={styles.label}>Voltage</Text>
        <Text style={styles.value}>{data.voltage.toFixed(2)}V</Text>
      </View>
      <View style={styles.card}>
        <Text style={styles.label}>Current</Text>
        <Text style={styles.value}>{data.current.toFixed(2)}A</Text>
      </View>
      <View style={styles.card}>
        <Text style={styles.label}>State of Charge</Text>
        <Text style={styles.value}>{data.soc}%</Text>
      </View>
      <View style={styles.card}>
        <Text style={styles.label}>Temperature</Text>
        <Text style={styles.value}>{data.temperature.toFixed(1)}°C</Text>
      </View>
    </View>
  );
}

Step 5: Secure Discharge Control

This is the critical feature that the BAT BMS app exposed without protection. Your implementation must require authentication:

“The difference between a safe BMS app and a dangerous one is exactly three lines of code: a password check, a user confirmation prompt, and encrypted BLE bonding. The BAT BMS app had none of these. When we audit BLE applications, these three gaps account for over 80% of critical vulnerabilities we find.” — Cybersecurity Team, DigiHaryana

async setDischarge(device: Device, enabled: boolean, password: string) {
  // SECURITY: Require authentication before state change
  const isAuthenticated = await this.verifyPassword(device, password);
  if (!isAuthenticated) {
    throw new Error('Authentication failed');
  }

  // Require user confirmation
  const confirmed = await this.promptUserConfirmation(enabled);
  if (!confirmed) return;

  const service = await device.services();
  const bmsService = service.find(s => s.uuid === BMS_SERVICE_UUID);
  const controlChar = await bmsService!.characteristic(CONTROL_CHAR_UUID);

  // Write discharge state (0x00 = OFF, 0x01 = ON)
  const command = enabled ? Buffer.from([0x01]) : Buffer.from([0x00]);
  await controlChar!.writeWithResponse(command.toString('base64'));
}

// Verify password against BMS-stored PIN
async verifyPassword(device: Device, password: string): Promise<boolean> {
  // Implementation depends on BMS manufacturer
  // Most support a password verification characteristic
  const service = await device.services();
  const bmsService = service.find(s => s.uuid === BMS_SERVICE_UUID);
  const pwdChar = await bmsService!.characteristic(PASSWORD_CHAR_UUID);

  const hash = this.hashPassword(password);
  await pwdChar!.writeWithResponse(hash.toString('base64'));

  // Read verification result
  const result = await pwdChar!.read();
  return result.value === '01';
}

async promptUserConfirmation(enabled: boolean): Promise<boolean> {
  return new Promise((resolve) => {
    Alert.alert(
      'Confirm Action',
      `Are you sure you want to ${enabled ? 'enable' : 'disable'} battery discharge?`,
      [
        { text: 'Cancel', onPress: () => resolve(false) },
        { text: 'Confirm', onPress: () => resolve(true) },
      ]
    );
  });
}

Step 6: Security Checklist for Production

Before releasing your BMS app, verify all of the following:

RequirementImplementation
BLE EncryptionUse bond: true during device connection to enable encrypted BLE pairing
Password ProtectionRequire a PIN/password for every control command, not just on first connect
User ConfirmationShow a confirmation dialog before any state-changing operation
Timeout SessionsAuto-disconnect after 5 minutes of inactivity
Rate LimitingLimit control commands to prevent brute-force attacks
Audit LoggingLog all control commands with timestamps locally

Complete Architecture

Your app architecture should look like this:

src/
├── services/
│   ├── bleManager.ts        # BLE connection, scanning, data parsing
│   ├── authManager.ts       # Password verification, session management
│   └── storageManager.ts    # Local data persistence
├── screens/
│   ├── ScanScreen.tsx       # BMS device discovery
│   ├── DashboardScreen.tsx  # Real-time telemetry display
│   ├── ControlsScreen.tsx   # Discharge control with auth
│   └── SettingsScreen.tsx   # BMS configuration
├── components/
│   ├── BatteryGauge.tsx     # Visual battery indicator
│   ├── CellMeter.tsx        # Individual cell voltage display
│   └── SecurityPrompt.tsx   # Password confirmation dialog
└── utils/
    ├── bmsProtocol.ts       # BMS data protocol parsing
    ├── crypto.ts            # Password hashing utilities
    └── constants.ts         # UUIDs and configuration

Testing Without Hardware

If you do not have a physical BMS unit for testing, use BLE simulation tools:

  • nRF Connect (Android/iOS) — Scan and simulate BLE devices
  • ble-simulation (Node.js) — Create a virtual BMS advertising the standard UUIDs
  • LightBlue (iOS/macOS) — Explore and debug BLE services

Here is a simple Node.js BMS simulator:

const { Peripheral } = require('@abandonware/noble');

const bms = new Peripheral();
bms.advertising = {
  localName: 'BMS-1234',
  serviceUuids: ['ffe0'],
};
bms.connect = async () => { /* simulate connection */ };

Publishing Considerations

When publishing your BMS app to Google Play or the Apple App Store:

  1. Disclose BLE usage clearly in your privacy policy
  2. Emphasise security features in your app description
  3. Add a safety disclaimer that the app controls real battery hardware
  4. Test on multiple BMS variants — protocols differ between manufacturers

Cross-Platform Alternatives

FrameworkBLE LibraryPros
React Nativereact-native-ble-plxMature, active community
Flutterflutter_blue_plusGood Dart integration
Kotlin (Android)Android BLE APINative performance
Swift (iOS)CoreBluetoothBest iOS support

Build Your BLE App with DigiHaryana

This guide gives you the foundation, but building a production-grade BLE application takes experience across hardware integration, security architecture, and cross-platform development.

Need a custom BLE app, IoT platform, or BMS monitoring solution? Our team at DigiHaryana has built Bluetooth-enabled applications for EV fleet management, industrial IoT monitoring, and smart energy systems. We handle the full lifecycle — from hardware selection and BLE protocol design to app development and Play Store deployment.

Contact us today to discuss your project — whether you need a complete app built from scratch or security-hardening for an existing BLE application.

Book a free consultation — we will analyse your requirements and provide a detailed project estimate within 48 hours.

Chat on WhatsApp — Get instant answers about our app development services.

Conclusion

Building a BMS monitoring app is a solid introduction to BLE development and IoT. The key lesson from the BAT BMS controversy is clear: authentication and authorisation are not optional for apps that control physical hardware. A BMS app without security is not just a bad product — it is a safety hazard.

Frequently Asked Questions

Q1: Can I build an app like BAT BMS legally? A1: Yes. Building a BMS monitoring app for legitimate battery management is completely legal. Always implement authentication and encryption.

Q2: What programming language should I use for a BLE BMS app? A2: React Native with react-native-ble-plx is a strong choice for cross-platform. Flutter with flutter_blue_plus is also good.

Q3: Do I need special hardware to test BMS communication? A3: Yes, a Bluetooth-enabled BMS unit (JBD, Xiaoxiang, Daly) is needed. Available for ₹1,000-₹5,000 on Indian electronics suppliers.

Q4: How do I get the BLE UUIDs for BMS communication? A4: Use BLE scanning tools like nRF Connect or LightBlue to discover the UUIDs advertised by your specific BMS model.

Q5: What security features should every BMS app have? A5: At minimum: password-protected pairing, encrypted BLE communication, user confirmation prompts for all control commands, session timeouts, and rate limiting.

Need Help With This?

Get Professional App Development Services

Cross-platform and native mobile apps with 4.8★ ratings — Flutter, React Native, Swift, Kotlin.

WhatsApp