Kotlin BLE Demo: Send Command and Display Device Response


Kotlin BLE Demo: Send a Command and Display the Device Response

This page is based on Kotlin BLE Demo-251218.txt. It shows how to use Kotlin + Android BLE GATT to connect to an OP-BT / OP-BTS device, send the command BaudTran,9600,N,8,1, and display the response (for example: OK,VER=01.00) in the UI.

1. Environment Setup

  • IDE: Android Studio (recommended with JDK 11 / Java 11)
  • Create a new project:
    • Name: OPManagerDemo
    • Template: Empty Activity
    • Language: Kotlin
    • Minimum SDK: Android 8.0 (API 26) or higher (recommended for BLE demos)

Source Code

The complete source code for this demo is available on GitHub:

You can clone the repository and open it directly in Android Studio:

git clone https://github.com/zenovate-team/OPManagerDemo-kotlin.git
cd OPManagerDemo-kotlin

2. Demo Overview

2.1 Features

  1. Scan nearby BLE devices
  2. Show devices (name + MAC address) in a list
  3. Connect to a selected device via BLE GATT
  4. Send the command BaudTran,9600,N,8,1 to the device
  5. Receive and display the device response, e.g. OK,VER=01.00
  6. Show connection status, sent commands, and received data as logs in the UI

2.2 Libraries / APIs

  • Bluetooth (BLE GATT):
    • BluetoothLeScanner
    • BluetoothGatt / BluetoothGattCallback
    • BluetoothGattCharacteristic / BluetoothGattDescriptor
  • Runtime permissions:
    • AndroidX Activity Result API: ActivityResultContracts.RequestMultiplePermissions

2.3 Protocol & UUID

  • Text command:

    • Send: BaudTran,9600,N,8,1
    • Example response: OK,VER=01.00
  • GATT UUID config (16-bit / 32-bit / 128-bit are supported):

    • Service UUID: 18F0
    • Notification Characteristic UUID: 2AF0
    • Write Characteristic UUID: 2AF1

3. Screenshots

Device scan screen: list nearby BLE devices and connect

Device scan list screen

Connection detail screen: enter command and show logs

Command and response log screen

4. Android Permissions (AndroidManifest.xml)

Edit app/src/main/AndroidManifest.xml with content similar to:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <!-- Bluetooth permissions (compatible with pre/post Android 12) -->
    <!-- Legacy Bluetooth/location permissions for Android 11 and below -->
    <uses-permission
        android:name="android.permission.BLUETOOTH"
        android:maxSdkVersion="30" />
    <uses-permission
        android:name="android.permission.BLUETOOTH_ADMIN"
        android:maxSdkVersion="30" />
    <uses-permission
        android:name="android.permission.ACCESS_COARSE_LOCATION"
        android:maxSdkVersion="30" />
    <uses-permission
        android:name="android.permission.ACCESS_FINE_LOCATION"
        android:maxSdkVersion="30" />

    <!-- New Bluetooth runtime permissions for Android 12+ -->
    <uses-permission
        android:name="android.permission.BLUETOOTH_SCAN"
        android:usesPermissionFlags="neverForLocation" />
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />

    <!-- Bluetooth / BLE hardware capabilities (optional) -->
    <uses-feature
        android:name="android.hardware.bluetooth"
        android:required="false" />
    <uses-feature
        android:name="android.hardware.bluetooth_le"
        android:required="false" />

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.OPManagerDemo">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.OPManagerDemo">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Runtime permission requests are implemented in Kotlin using ActivityResultContracts.RequestMultiplePermissions (see next section).

5. Key Kotlin Code Snippets

The full source is in Kotlin BLE Demo-251218.txt. Below are only the key parts related to UUID config, connection, and sending the command:

object BleConfig {
    const val SERVICE_UUID_STR = "18f0"
    const val WRITE_CHAR_UUID_STR = "2af1"
    const val NOTIFY_CHAR_UUID_STR = "2af0"
    // Helper to convert 16-bit / 32-bit / 128-bit strings into UUID
}

class BleConnectionManager(private val context: Context) {
    // Fields: BluetoothManager, BluetoothAdapter, BluetoothGatt, writeCharacteristic ...

    fun connect(
        address: String,
        onStateChange: (ConnectionState) -> Unit,
        onLog: (ConnectionLog) -> Unit
    ) {
        // 1. Close previous GATT if any
        // 2. Get remote device by MAC address
        // 3. Connect with connectGatt(...)
        // 4. Discover services, find service/characteristic by UUID
        // 5. Enable notification (CCCD) on the write/notify characteristic
        // See full implementation in Kotlin BLE Demo-251218.txt
    }

    fun send(
        text: String,
        onLog: (ConnectionLog) -> Unit
    ) {
        // Append CRLF because many modules expect serial-like protocol
        val payload = text + "\r\n"
        // gatt.writeCharacteristic(writeCharacteristic)
        onLog(ConnectionLog(LogType.SEND, "Sending: $text"))
    }
}

@Composable
private fun ConnectionDetailScreen(
    device: BluetoothDeviceUi?,
    connectionState: ConnectionState,
    logs: List<ConnectionLog>,
    onBack: () -> Unit,
    onReconnect: () -> Unit,
    onSendCommand: (String) -> Unit
) {
    // Default command for this demo
    var commandText by remember { mutableStateOf("BaudTran,9600,N,8,1") }
    // UI shows: connection state, device name, command input, Send button, and logs list
}

6. How to Run

  1. Connect your Android phone to the PC with a USB cable and enable USB debugging on the device
  2. In Android Studio, click Run ▶ and choose your device
  3. After the app starts:
    • Grant all requested Bluetooth / location permissions on first launch
    • On the device list screen, tap Scan (top-right) to scan BLE devices
    • Tap your OP-BT / OP-BTS device in the list to connect
  4. On the “Connection Detail” screen:
    • Keep the default command BaudTran,9600,N,8,1 and tap Send
    • You should see logs for the sent command and the device response, e.g. OK,VER=01.00