Select Empty ActivityAndroid Empty Activity

Interfacing hc-05 bluetooth module with Android Application is one of the very basic task. Whenever we need to communicate with our Arduino based devices or any other embedded system project where we need to control devices with bluetooth. Bluetooth modules are one of the very important and basic wireless communication protocol which we can easily implement due to the HC-05 module. There are also other bluetooth modules available like HC-06 any other HC series bluetooth module variations.

Learning to interface the Bluetooth module in your Android Kotlin based application will allow you to make native applications for your next automation projects. Either you are planning to collect sensors data in your Android application or controlling appliances through your android application over bluetooth this will help you.

In this tutorial we are going to explain how to communicate with your hc-05 bluetooth module or any other similar bluetooth module through android application. For the demonstration purpose we are using the Kotlin code but the similar approach could be implemented in Java language as well. We will explain how to take permissions for bluetooth in your android application. How to send data over bluetooth in your android application. Last but not the least, one of the most complex looking thing which is to receive data from bluetooth module in your android application. So let’s dive in.

Step1: Create a New Application in Android Studio

First of all you need to create a new project in your android studio. We are going to implement the basic lagacy xml layout style activity and previous gradle build system. So look into the image below and carefully create a new project in your android studio for the bluetooth communication. Once you click on create new project it will take you to the Activity selection. You should Select the Blank activity but no the one which have the jetpack compose icon in it. In this application we are not demonstrating the jetpack compose. Rather we are focusing on the XML based layout.

Select Empty Activity
Android Empty Activity

Step2: Select Groovy DSL(gradle.builde)

For the build system you should select the Groovy DSL (gradle.build) system. You can choose the previous kotlin based default system but just to follow along select this one.

select the gradle build system

Step3: Add Permissions in AndroidMenifest.xml file

Now you need to go to your android menifest file and copy the following permissions for your bluetooth. Which you also have to ask permissions for in the next step.

<uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    Code language: HTML, XML (xml)

Step4: Create a new file PermissionManager

Because in higher android versions we have to ask the user consent or the permissions required on the runtime. Also the user have control over to allow or disallow the permissions. We need to ask for permissions. There are many ways to do this. Also there are easypermission library for this purpose. But I preffer this custom solution making own permission manager class. This solution is not efficient or complete but it do the task. I test this application in my Techno 10 Pro mobile running on Android 13. So let’s create a new Kotlin class, name it `PermissionManager` and copy this code which is taken from this GitHub repository.

package com.example.myapplication2


import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import androidx.core.app.ActivityCompat

class PermissionManager(private var context: Context) {
    private val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        arrayOf(
            Manifest.permission.BLUETOOTH,
            Manifest.permission.BLUETOOTH_SCAN,
            Manifest.permission.BLUETOOTH_ADMIN,
            Manifest.permission.BLUETOOTH_CONNECT,
            Manifest.permission.ACCESS_COARSE_LOCATION,
            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.WRITE_EXTERNAL_STORAGE,
            Manifest.permission.CALL_PHONE,
            Manifest.permission.RECORD_AUDIO,

        )
    } else {
        TODO("VERSION.SDK_INT < S")
    }


    private val PERMISSION_GRANTED = PackageManager.PERMISSION_GRANTED

    fun requestPermissions() {
        for (permission in permissions) {
            if(ActivityCompat.checkSelfPermission(context, permission) != PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(context as Activity, permissions, 5)
            }
            else {
                Log.d("PERMISSIONS", "Permission $permission Granted")
            }
        }
    }
}Code language: HTML, XML (xml)

Step 4: Ask Permissions

Now that you have your permission manager class you can ask user for permissions but first let’s create the instance of this permission manager class in your main activity. You can simple do this by writing the following code.

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        PermissionManager(this).requestPermissions()
}Code language: JavaScript (javascript)

Also make sure to suppress the missing permission warnings which will help you a lot for complex version check warnings which we are not implementing here for the sake of simplicity. Here is how you can do this

@SuppressLint("MissingPermission")
class MainActivity : AppCompatActivity() {
}Code language: CSS (css)

Create the RF Socket

We need to create a socket from where we can communicate to the Bluetooth device. For this purpose let’s create a separate class or you can implement it inside the same activity for the sake of simplicity. Here is the example of doing this.

package com.example.myapplication2


import android.annotation.SuppressLint
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothSocket
import android.util.Log
import java.io.IOException
import java.util.UUID

@SuppressLint("MissingPermission")
class BluetoothHandler(
    private val bluetoothAdapter: BluetoothAdapter,
    private val macAddress: String
) : Thread() {
    fun createSocket(device: BluetoothDevice): BluetoothSocket {
        val uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
        return device.createRfcommSocketToServiceRecord(uuid)
    }


    override fun run() {
        var bluetoothSocket: BluetoothSocket? = null
        if(bluetoothSocket == null || !bluetoothSocket.isConnected) {
            val device = bluetoothAdapter.getRemoteDevice(macAddress)
            try {
                bluetoothSocket = createSocket(device)
            }catch (e: IOException) {
                Log.d("Connect Error", "Error Creating Socket!")
            }

            if(bluetoothSocket != null && bluetoothSocket.isConnected) {
                Log.d("Connecting", "Connecting to ${device.name}")
            } else {
                bluetoothSocket?.close()
            }
        }
    }
}

Get the Input and Output Streams

For the transmission purpose and receiving data, you need to get the outputstream and input stream from your socket. for this you can create a `lateinit` variable for and can instantiate this later from your Bluetooth adapter. Here is how you can do this.

private lateinit var outputStream: OutputStream
private lateinit var inputStream: InputStream
Code language: PHP (php)

Once you connected to your hc-05 module you can simply check if the device is connected you can get the streams like this.


      // Set up InputStream after BluetoothSocket is connected
        if (bluetoothSocket.isConnected) {
            inputStream = bluetoothSocket.inputStream
            outputStream = bluetoothSocket.outputStream
        }Code language: JavaScript (javascript)

Send Data to HC-05 Bluetooth Module

Now that we have the input and output streams we can utlize it for send data and receiving data as well. First of all we are simply demonstrating how to transmit data over bluetooth because that is the simplest part and will help you to grab the basics of handling streams. Here is a function which we created for simple data transmission over bluetooth rf socket stream.

 private fun sendToHC05(valToSend: String) {
        try {
            // Check if the BluetoothSocket is connected
            if (bluetoothSocket.isConnected) {
                // Convert the string to bytes and send
                outputStream.write(valToSend.toByteArray())
                outputStream.flush()

            } else {
                // Handle the case where the BluetoothSocket is not connected
                // Maybe show an error message or try to reconnect
                Toast.makeText(baseContext,"Not Connected to any device",Toast.LENGTH_SHORT).show()
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }Code language: PHP (php)

Here you can see we simple used the outputstream.write() function to write our desired string by converting it to ByteArray with the help of extension function in the String Class in Kotlin. The reason for converting it to ByteArray is because this is the datatype which the stream accept and return in reading case.

Receive data from Bluetooth

Now that we are done with the data transmission we are ready to receive the data. The data receiving is an ongoing process where we have to wait for the data. This is a polling method which we cannot implement on main UI thread. So we need to create a seperate thread for this purpose just the same way we do for creating a connection with the HC-05 device. Here is how we can implement the data receiving thread in Kotlin.

// Set up InputStream after BluetoothSocket is connected
        if (bluetoothSocket.isConnected) {
            inputStream = bluetoothSocket.inputStream

            // Start a thread to continuously read data from InputStream
            Thread {
                val buffer = ByteArray(1024)
                var bytes: Int

                while (true) {
                    try {
                        // Read from the InputStream
                        bytes = inputStream.read(buffer)

                        // Convert the bytes to a string (assuming it's text data)
                        var receivedData = String(buffer, 0, bytes)
                        receivedData  = receivedData.trim()

                        // Handle received data here, e.g., update UI or perform actions
                        runOnUiThread {
                            val lastValue = getLastValue(receivedData)
                            val intValue: Float =
                                (lastValue.toFloatOrNull() ?: 0.0).toFloat() // Convert string to integer, default to 0 if conversion fails
                            resultTextView.text = "Smoke: $lastValue ppm"
                            val thresholdValue:Float =
                                (edtThresholdValue.text.toString().toFloatOrNull() ?: 3.0).toFloat()
                            if (intValue > thresholdValue ) {
                                val phoneNumber = edtNumberToCall.text.toString()
                                val callIntent = Intent(Intent.ACTION_CALL, Uri.parse("tel:$phoneNumber"))

                                // Check if the CALL_PHONE permission is granted before making the call
                                if (checkSelfPermission(android.Manifest.permission.CALL_PHONE) == android.content.pm.PackageManager.PERMISSION_GRANTED) {
                                    startActivity(callIntent)
                                } else {
                                    // Request the CALL_PHONE permission
                                    requestPermissions(arrayOf(android.Manifest.permission.CALL_PHONE), 1)
                                }
                            }
                           // Toast.makeText(baseContext, "Get:$receivedData",Toast.LENGTH_SHORT).show()
                        }
                    } catch (e: IOException) {
                        // Handle exceptions when reading from InputStream
                        e.printStackTrace()
                        break
                    }
                }
            }.start()
        } else {
            // Handle the case where BluetoothSocket is not connected
            // ...
            Toast.makeText(baseContext,"Input Stream not connected",Toast.LENGTH_SHORT).show()
        }
Code language: JavaScript (javascript)

By Abdul Rehman

My name is Abdul Rehman and I love to do Reasearch in Embedded Systems, Artificial Intelligence, Computer Vision and Engineering related fields. With 10+ years of experience in Research and Development field in Embedded systems I touched lot of technologies including Web development, and Mobile Application development. Now with the help of Social Presence, I like to share my knowledge and to document everything I learned and still learning.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.