This codelab reinforces the content learned in the last lab and shows you how to develop an IOT android application using MQTT. For this lab we'd be using the HiveMQ MQTT Client library for android.
Most Android-powered devices have built-in sensors that measure motion, orientation, and various environmental conditions. These sensors are capable of providing raw data with high precision and accuracy, and are useful for general monitoring. For example:
The objective of this lab is to develop an application that uses the device's proximity sensor to determine if the device is close to, or far away from an object. In most phones, the proximity sensor is located near the phone's front facing camera. There are a lot of applications that use this sensor. For example
Create a new application. I'm going to name my app, ProximitySensorLab

The initial user interface should contain the following

This user interface can easily be achieved if the following steps are made
Hello World text an ID so that we can change the words of the TextView in codeTry this yourself before going to the solutions below
Contents of my activity_main.xml file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_sensor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Text that describes the sensor's state here!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_disable" />
<Button
android:id="@+id/btn_enable"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="40dp"
android:layout_marginTop="44dp"
android:text="Enable Sensor"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_disable"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="44dp"
android:layout_marginEnd="40dp"
android:text="Disable Sensor"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/btn_enable"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
With the initial draft of the user interface done, we can now integrate the sensor's functionality into the app. This can be done as follows:
To access the proximity sensor, we need to list the feature-set in the AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- Add the line below -->
<uses-feature android:name="android.hardware.sensor.proximity" android:required="true" />
<application
<!-- Redacted -->
Inside our ActivityMain.kt, create two instance variables as follows
// Add the following two lines
private lateinit var sensorManager: SensorManager
private var proximity: Sensor? = null
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
// Add the lines below
// Get an instance of the sensor service, and use that to get an instance of
// the proximity sensor.
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
proximity = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)
}
Android has an interface called SensorEventListener. Modify the MainActivity.kt as follows
import android.hardware.SensorEventListener
MainActivity to implement the interfaceclass MainActivity : AppCompatActivity(), SensorEventListener {
//redacted
}
override fun onSensorChanged(event: SensorEvent?) {
TODO("Not yet implemented")
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
Log.e("SENSOR", "Accuracy changed")
}
private lateinit var sensorManager: SensorManager
private var proximity: Sensor? = null
// Add the following line
private var isEnabled: Boolean = false
fun enableSensor(view: View?){
if (isEnabled){
Toast.makeText(this, "Sensor already enabled", Toast.LENGTH_SHORT).show()
return
}
proximity?.also { proximity ->
sensorManager.registerListener(this, proximity, SensorManager.SENSOR_DELAY_NORMAL)
}
}
fun disableSensor(view: View?){
if (!isEnabled){
Toast.makeText(this, "Sensor already disabled", Toast.LENGTH_SHORT).show()
return;
}
sensorManager.unregisterListener(this)
isEnabled = false
findViewById<TextView>(R.id.tv_sensor).text = "The phone is not receiving data from the proximity sensor"
}
We've currently laid the framework for handling the sensor data but there are still a few things outstanding. We need to link the user interface to the functionality This can be done as follows.
enableSensor function.enableSensor function.Update the activity_main.xml file and add an onClick property to the "Enable Sensor" button.
<Button
android:id="@+id/btn_enable"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="40dp"
android:layout_marginTop="44dp"
android:onClick="enableSensor"
android:text="Enable Sensor"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Update the activity_main.xml file and add an onClick property to the "Disable Sensor" button.
<Button
android:id="@+id/btn_disable"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="44dp"
android:layout_marginEnd="40dp"
android:text="Disable Sensor"
android:onClick="disableSensor"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/btn_enable"
app:layout_constraintTop_toTopOf="parent" />
The sensor gives us data in the onSensorChanged function. The data that comes back is the distance to the nearest object.
Update the onSensorChanged function as follows
override fun onSensorChanged(event: SensorEvent?) {
if (event == null) return
if (event.sensor.type != Sensor.TYPE_PROXIMITY) return
val distanceToNearestObject = event.values[0]
val textView = findViewById<TextView>(R.id.tv_sensor)
Log.e("SENSOR", "DISTANCE = $distanceToNearestObject")
if (distanceToNearestObject < event.sensor.maximumRange){
textView.text = "The phone is near an object"
} else {
textView.text = "The phone is not near an object"
}
}
Currently we have an android app that is fully functional. It tells us whether the phone is close to a device or not. We need to build upon this functionality and integrate the MQTT functionality to notify the broker.
The steps we need to perform are as follows
Gradle Scripts in the explorer panelbuild.gradle.kts (Module: app) fileimplementation(libs.hivemq.mqtt.client) in the dependencies block OR implementation("com.hivemq:hivemq-mqtt-client:1.3.3")packagingOptions { resources.excludes.add("META-INF/*") }
Add the internet permission to the manirfest
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-feature android:name="android.hardware.sensor.proximity" android:required="true" />
<uses-permission android:name="android.permission.INTERNET"/>
<application
<!-- redacted -->
Add a new instance variable to the MainActivity class.
private lateinit var sensorManager: SensorManager
private var proximity: Sensor? = null
private var isEnabled: Boolean = false
// Add the line below
private var client: Mqtt5BlockingClient? = null
override fun onCreate(savedInstanceState: Bundle?) {
//redacted
}
Inside the onCreate in MainActivity.kt, create a new client that will handle the MQTT calls
// Get an instance of the sensor service, and use that to get an instance of
// the proximity sensor.
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
proximity = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)
//Add the lines below
client = Mqtt5Client.builder()
.identifier(UUID.randomUUID().toString())
.serverHost("broker.sundaebytestt.com")
.serverPort(1883)
.build()
.toBlocking()
Modify the enableSensor function, let it also connect to the broker once the sensor is enabled
proximity?.also { proximity ->
sensorManager.registerListener(this, proximity, SensorManager.SENSOR_DELAY_UI)
isEnabled = true
//Add the lines of code below
try {
client?.connect()
} catch (e:Exception){
Toast.makeText(this,"An error occurred when connecting to broker", Toast.LENGTH_SHORT).show()
}
}
For this lab, we will send messages to the topic lab/proximity
Modify the onSensorChanged function to send a message on that topic to the broker
override fun onSensorChanged(event: SensorEvent?) {
if (event == null) return
if (event.sensor.type != Sensor.TYPE_PROXIMITY) return
val distanceToNearestObject = event.values[0]
val textView = findViewById<TextView>(R.id.tv_sensor)
Log.e("SENSOR", "DISTANCE = $distanceToNearestObject")
val textToSend = if (distanceToNearestObject < event.sensor.maximumRange) {
"Kwasi's phone is near an object"
} else {
"Kwasi's phone is NOT near an object"
}
textView.text = textToSend
try{
client?.publishWith()?.topic("lab/proximity")?.payload(textToSend.toByteArray())?.send()
} catch (e:Exception){
Toast.makeText(this,"An error occurred when sending a message to the broker", Toast.LENGTH_SHORT).show()
}
}
We want to disconnect from the broker when we stop listening to sensor updates. Update the disconnect function as follows
fun disableSensor(view: View?){
if (!isEnabled){
Toast.makeText(this, "Sensor already disabled", Toast.LENGTH_SHORT).show()
return;
}
sensorManager.unregisterListener(this)
isEnabled = false
findViewById<TextView>(R.id.tv_sensor).text = "The phone is not receiving data from the proximity sensor"
try {
client?.disconnect()
} catch (e:Exception){
Toast.makeText(this,"An error occurred when disconnecting from broker", Toast.LENGTH_SHORT).show()
}
}
https://developer.android.com/develop/sensors-and-location/sensors/sensors_overview https://hivemq.github.io/hivemq-mqtt-client/docs/installation/