Bt Socket closed exception - android

I am building an app that is supposed to send a file over a Bluetooth socket but for some reason i keep getting the same Exception. The error message says that the socket is already closed but i don't understand how or why it is getting closed before i attempt to read from it. The user is presented with an AlertDialog with options to choose from. they are supposed to: 1. Pair to a device. 2. send file to device (currently labeled 'connect' in dialog). The exception occurs after the devices are paired, when the connect option is selected. when the receiving device attempts to read from socket i get this exception:
Process: com.example.zemcd.toofxchange, PID: 1074
java.io.IOException: bt socket closed, read return: -1
at android.bluetooth.BluetoothSocket.read(BluetoothSocket.java:588)
at android.bluetooth.BluetoothInputStream.read(BluetoothInputStream.java:96)
at java.io.BufferedInputStream.read1(BufferedInputStream.java:273)
at java.io.BufferedInputStream.read(BufferedInputStream.java:334)
at java.io.FilterInputStream.read(FilterInputStream.java:107)
at kotlin.io.ByteStreamsKt.copyTo(IOStreams.kt:101)
at kotlin.io.ByteStreamsKt.copyTo$default(IOStreams.kt:98)
at kotlin.io.ByteStreamsKt.readBytes(IOStreams.kt:117)
at com.example.zemcd.toofxchange.BluetoothUtils$Companion.receiveFile(BluetoothUtils.kt:82)
at com.example.zemcd.toofxchange.ListenThread$run$acceptThread$1.run(BluetoothUtils.kt:104)
at java.lang.Thread.run(Thread.java:761)
below are the functions used for pair, unpair, and connect (to send data) along with the threads subclasses that used:
class BluetoothUtils {
companion object {
var listener: ListenThread? = null
val _UUID = UUID.fromString("a0e7e4c7-0e4e-43b7-9d18-659192512164")
val TAG = "BluetoothUtils"
val receiver = MainBTStatusReceiver()
fun initPairingServer(adapter: BluetoothAdapter){
var mmServerSocket: BluetoothServerSocket?
try {
var tmp = adapter.listenUsingRfcommWithServiceRecord(TAG, _UUID)
mmServerSocket = tmp
listener = ListenThread(mmServerSocket)
listener!!.start()
}catch (ioe: IOException){
Log.e(TAG, "Error initializing Bluetooth", ioe)
}
}
fun cancelListener() = listener!!.cancel()
fun connect(adapter: BluetoothAdapter, device: BluetoothDevice):Unit{
var btSocket: BluetoothSocket?
Log.d(TAG, "connect function called")
if (device.bondState==BluetoothDevice.BOND_NONE)return //to prompt user to pair
try {
adapter.cancelDiscovery()
btSocket = device.createRfcommSocketToServiceRecord(_UUID)
ConnectThread(btSocket).start()
}catch (ioe: IOException){
Log.e(TAG, "error connecting", ioe)
}
}
fun startPair(adapter: BluetoothAdapter, device: BluetoothDevice): Unit{
adapter.cancelDiscovery()
Log.d(TAG, device.bondState.toString())
device.createBond()
}
fun unPair(device: BluetoothDevice): Any = device::class.java.getMethod("removeBond").invoke(device)
fun sendFile(btSocket: BluetoothSocket){
val out = btSocket.outputStream.buffered()
out.use {
val msg = "hello".toByteArray()
Log.d(TAG, "sending data")
it.write(msg, 0, msg.size)
it.flush()
}
btSocket.close()
}
fun receiveFile(btSocket: BluetoothSocket){
Log.d(TAG, "receiveFile called")
val inStream = btSocket.inputStream.buffered()
//val bytes: ByteArray = ByteArray(1024)
val bytes: ByteArray = inStream.use { it.readBytes(1024)}
Log.d(TAG, bytes.toString())
btSocket.close()
}
}
}
class ListenThread(val btServSock: BluetoothServerSocket) : Thread(){
companion object {
val TAG = "ListenThread"
}
var btSocket: BluetoothSocket? = null
override fun run() {
super.run()
while (true){
try {
Log.d(TAG, "listening . . . ")
btSocket = btServSock.accept()
Log.d(TAG, btSocket.toString() + " was accepted")
val acceptThread = Thread(Runnable { BluetoothUtils.receiveFile(btSocket!!) })
acceptThread.start()
}catch (ioe: IOException){
Log.e(TAG, "Error", ioe)
break
}
}
}
fun cancel() = btServSock.close()
}
class ConnectThread(val btSocket: BluetoothSocket) : Thread(){
companion object {
val TAG = "Pairing Thread"
}
override fun run() {
super.run()
try {
Log.d(TAG, "attempting to connect")
btSocket.connect()
BluetoothUtils.sendFile(btSocket)
}catch (ioe: IOException){
Log.e(TAG, "error connecting", ioe)
btSocket.close()
}
}
}
and they are called like this inside of OnClickListener:
ops.forEach { it.setOnClickListener {
Toast.makeText(it.context, it.id.toString(), Toast.LENGTH_SHORT).show()
when(it.id){
R.id.statOp -> {}
R.id.connectOp -> {
Log.d(TAG, "connectOp reached")
BluetoothUtils.connect(BluetoothAdapter.getDefaultAdapter(), btDevice)
dialog!!.dismiss()
}
R.id.pairOp -> {
Log.d(TAG, "pairOp reached")
mReceiver.setFocus(this#DeviceHolder)
BluetoothUtils.startPair(BluetoothAdapter.getDefaultAdapter(), btDevice)
Log.d(TAG, "start pair complete")
dialog!!.dismiss()
}
R.id.unPairOp -> {
Log.d(TAG, "unPairOp reached")
mReceiver.setFocus(this#DeviceHolder)
BluetoothUtils.unPair(btDevice)
Log.d(TAG, "unpair complete")
dialog!!.dismiss()
}
R.id.sendOp -> {}
}
} }
and here is my with resources function:
inline fun <T: AutoCloseable, U> withResources(resource: T, fn: (T) -> U) : U{
try {
return fn(resource)
}finally {
resource.close()
}
}
please help me find my mistake. if other pieces of my code are needed let me know and i will post them. thank you for your help.

Related

How to connect BT barcode reader correctly?

I have two type of BT reader on of them is a Symbol CS3070 and the other one is a Datalogic DBT6400.
For the DBT6400 device I found an Android SDK with which the connection is very simple. But for the first one there is no supported SDK.
So I decided to connect to these device directly without SDK. Everything works fine,until the device disconnected because of sleep mode or because of I switch from one to another device.
In this case I get a lot of exception inside the while loop, because of the socket is closed. I have a tip for this why it's happen, maybe because of the coroutine, if it is possible.
UPDATE: So I found the problem, a put the catch block this line of code: reader=0, and there is only 1 error, so it's seems good. But I still don't know, is it a good solution?
My CustomReader class
class CustomReader(
private val deviceName: String,
onBarcodeRead: (barcode: String?) -> Unit
) : BarcodeReader(onBarcodeRead), CoroutineScope {
private val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
private var pairedDevices: Set<BluetoothDevice>? = null
private var device: BluetoothDevice? = null
private lateinit var uuid: UUID
private var socket: BluetoothSocket? = null
private var connected: Boolean = false
private val localJob: Job = Job()
override val coroutineContext: CoroutineContext
get() = Dispatchers.IO + localJob
override fun init(context: Context) {
pairedDevices = bluetoothAdapter.bondedDevices
}
override fun connect(address: String) {
try {
bluetoothAdapter.startDiscovery()
uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
device = pairedDevices?.firstOrNull { x -> x.name.startsWith(deviceName) }
if(device == null){
Timber.d("CustomReader: $deviceName: Device not found")
return
}
initSocket()
launch {
run()
}
} catch (e: Exception) {
Timber.e(e)
}
}
override fun disconnect() {
try {
socket?.close()
socket = null
} catch (e: IOException) {
Timber.d("CustomReader: $deviceName: Socket closed")
}
localJob.cancel()
}
override fun destroy() {
try {
socket?.close()
socket = null
} catch (e: IOException) {
Timber.d("CustomReader: $deviceName: Socket closed")
}
localJob.cancel()
}
override fun getAddress(): String? {
return device?.address
}
override fun isConnected(): Boolean {
return connected
}
private suspend fun run() {
try {
socket?.let { socket ->
socket.connect()
connected = true
val buffer = ByteArray(1024)
var read = 0
do {
try {
read = socket.inputStream.read(buffer)
val data = String(buffer, 0, read)
onBarcodeRead.invoke(data)
} catch (e: Exception) {
Timber.e(e)
}
} while (read > 0)
CoroutineScope()
}
}catch (e: Exception){
Timber.d("CustomReader: $deviceName Disconnected")
connected = false
}
}
fun initSocket(){
if(socket != null)
return
socket = device?.createRfcommSocketToServiceRecord(uuid)
bluetoothAdapter.cancelDiscovery()
}
fun closeSocket(){
try {
socket?.close()
}catch (e: Exception){
Timber.d("CustomReader: $deviceName: Socket closed")
}
}
}
**BletoothHandlar class:**
object BluetoothHandler {
#JvmStatic
private var currentReader: BarcodeReader? = null
#JvmStatic
private val barcodeLiveData = MutableLiveData<EventWrapper<String?>>()
#JvmStatic
fun getData(): LiveData<EventWrapper<String?>> = barcodeLiveData
init {
fixedRateTimer(
name = "blueooth-reconnect",
daemon = true,
initialDelay = 0L,
period = 5000
) {
if (currentReader == null) {
Timber.d("No reader device connected")
return#fixedRateTimer
}
if (currentReader?.isConnected() == true) {
Timber.d("reader connected, doing nothing")
} else if (currentReader?.isConnected() == false) {
Timber.d("reader not connected, reconnecting")
currentReader?.connect(currentReader!!.getAddress()!!)
}
}
}
#JvmStatic
fun connectToDevice(device: BluetoothDeviceCustom, context: Context): BarcodeReader? {
if (device.address == currentReader?.getAddress() && currentReader?.isConnected() == true) {
Timber.d("device already connected")
return currentReader
}
//ha változott a kiválasztott olvasó
if (device.address != currentReader?.getAddress()) {
currentReader?.disconnect()
}
val reader = createReader(device)
if (reader == null) {
currentReader?.disconnect()
} else {
reader.init(context)
reader.connect(device.address)
}
currentReader = reader
return reader
}
#JvmStatic
fun disconnect() {
currentReader?.disconnect()
currentReader?.destroy()
currentReader = null
}
#JvmStatic
fun getConnectedDevice(): BarcodeReader? {
return currentReader
}
#JvmStatic
private fun createReader(bluetoothDeviceCustom: BluetoothDeviceCustom): BarcodeReader? {
return CustomReader(bluetoothDeviceCustom.name) {
barcodeLiveData.postValue(EventWrapper(it))
}
/*return if (bluetoothDeviceCustom.name.startsWith("DBT6400")) {
DatalogicDBT6400 {
barcodeLiveData.postValue(EventWrapper(it))
}
} else if (bluetoothDeviceCustom.name.startsWith("CS3070")) {
CustomReader(bluetoothDeviceCustom.name) {
barcodeLiveData.postValue(EventWrapper(it))
}
} else {
null
}*/
}
}
So, here it is my solution, I don't think it is the best, but it works.
const val UUID_STRING = "00001101-0000-1000-8000-00805F9B34FB"
class CustomReader(
onBarcodeRead: (barcode: String?) -> Unit
) : BarcodeReader(onBarcodeRead), CoroutineScope {
private val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
private var pairedDevices: Set<BluetoothDevice>? = null
private var device: BluetoothDevice? = null
private var socket: BluetoothSocket? = null
private val localJob: Job = Job()
private var deviceName = ""
private val uuid = UUID.fromString(UUID_STRING)
override val coroutineContext: CoroutineContext
get() = Dispatchers.IO + localJob
override fun init(context: Context) {
pairedDevices = bluetoothAdapter.bondedDevices
}
private val singleExecutor: ExecutorCoroutineDispatcher =
Executors.newFixedThreadPool(1).asCoroutineDispatcher()
override fun connect(address: String) {
try {
device = pairedDevices?.firstOrNull { x -> x.address == address }
if(device == null){
Timber.d("CustomReader: $deviceName: Device not found")
return
}
deviceName = device?.name ?: "N/A"
initSocket()
launch {
withContext(singleExecutor){
run()
}
}
} catch (e: Exception) {
Timber.e(e)
}
}
override fun disconnect() {
closeSocket()
localJob.cancel()
}
override fun getAddress(): String? {
return device?.address
}
override fun isConnected(): Boolean {
return socket?.isConnected ?: false
}
private suspend fun run() {
try {
socket?.let { socket ->
socket.connect()
val buffer = ByteArray(1024)
var read = 0
do {
try {
read = socket.inputStream.read(buffer)
val data = String(buffer, 0, read)
onBarcodeRead.invoke(data)
} catch (e: Exception) {
Timber.d("CustomReader: $deviceName Socket disconnected")
read = -1
closeSocket()
}
} while (read > 0)
}
}catch (e: Exception){
Timber.d("CustomReader : $deviceName Disconnected")
}
}
private fun closeSocket(){
try {
socket?.close()
}catch (e: IOException){
Timber.d("CustomReader: $deviceName Socket closed")
}
socket = null
}
private fun initSocket(){
if(socket != null)
return
socket = device?.createRfcommSocketToServiceRecord(uuid)
}
}

android Kotlin socket scanner.hasNext() has No response

Server-side language: Python
Client-side language: Kotlin, Android
It receives data from different lines at one line.
class SampleActivity : AppCompatActivity() {
private val TAG = "SAMPLE_ACTIVITY_TAG"
private lateinit var editTextMessage: EditText
private val IPs = listOf("192.168.100.30")
private val PORTs = listOf(5050)
private val INDEX_SERVER = 0
private var socket: Socket? = null
private val IP = IPs[INDEX_SERVER]
private val PORT = PORTs[INDEX_SERVER]
private lateinit var outPrintWriter: PrintWriter
private val CONNECTION_TOKEN = "fab2bd65f67193d761c06b07a708d3232f0d8569"
private val TAG_CONNECT_START = "[CONNECT]"
private val TAG_CONNECT_END = "[/CONNECT]"
private val TAG_TRANSFER_START = "[TRANSFER]"
private val TAG_TRANSFER_END = "[/TRANSFER]"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
editTextMessage = findViewById(R.id.editTextMessage)
CoroutineScope(Dispatchers.IO).launch {
connect()
}
buttonSubmit.setOnClickListener {
if(editTextMessage.text.toString().trim().isEmpty()){
return#setOnClickListener
}
CoroutineScope(Dispatchers.IO).launch {
send(messageConverter(TAG_TRANSFER_START + editTextMessage.text.toString().trim() + TAG_TRANSFER_END))
}
}
}
override fun onDestroy() {
outPrintWriter.close()
super.onDestroy()
}
private fun messageConverter(text: String): String {
Log.w(TAG, "[CONVERTING] \nMessage: $text\nAscii: ${text.toASCII()}")
return text.toASCII() // Defined in extention.kt
}
private suspend fun connect() {
try {
Log.w(TAG, "[CONNECTING] Connecting to: $IP:$PORT")
socket = Socket(IP, PORT)
socket?.receiveBufferSize = 1024
socket?.soTimeout = 1 * 60 * 60
outPrintWriter = PrintWriter(socket!!.getOutputStream())
send(messageConverter(TAG_CONNECT_START + CONNECTION_TOKEN + TAG_CONNECT_END))
receive()
} catch (uHE: UnknownHostException) {
Log.e(TAG, "[ERROR] UnknownHostException, ${uHE.message}", uHE)
} catch (eIO: IOException) {
Log.e(TAG, "[ERROR] IOException, ${eIO.message}", eIO)
} catch (e: Exception) {
Log.e(TAG, "[ERROR] Exception, ${e.message}", e)
}
}
private suspend fun send(text: String) {
try {
Log.w(TAG, "[SENDING] Message: $text")
outPrintWriter.print(text)
outPrintWriter.flush()
Log.w(TAG, "[SENDING] finished .")
clearMessageBox()
} catch (eIO: IOException) {
Log.e(TAG, "[ERROR] IOException, ${eIO.message}", eIO)
} catch (e: Exception) {
Log.e(TAG, "[ERROR] IOException, ${e.message}", e)
}
}
private suspend fun receive() {
try {
val scanner = Scanner(
socket?.getInputStream()
)
val lines = ArrayList<String>()
var line: String?
while (scanner.hasNextLine()) {
Logger.w(TAG, "[RECEIVING] Scanner hasNext")
line = scanner.nextLine()
if (!line.isNullOrEmpty()) {
lines.add(line)
}
}
Logger.w(TAG, "[RECEIVING] Exit While loop : ${lines}")
} catch (eIO: IOException) {
Log.e(TAG, "[ERROR] Receive IOException error: ${eIO.message}", eIO)
} catch (e: Exception) {
Log.e(TAG, "[ERROR] Receive Exception error: ${e.message}", e)
}
}
private suspend fun clearMessageBox() {
withContext(Main) {
editTextMessage.text.clear()
}
}
}
Logs:
[CONNECTING] Connecting to: 192.168.100.30:5050
[CONVERTING]
Message: [CONNECT]fab2bd65f67193d761c06b07a708d3232f0d8569[/CONNECT]
Ascii: 91,67,79,78,78,69,67,84,93,102,97,98,50,98,51,50,102,48,100,49,55,56,99,48,54,55,48,56,53,55,97,100,54,53,102,54,54,98,48,100,51,50,57,49,57,51,100,55,54,91,47,67,79,78,78,69,67,84,93
[SENDING] Message: 91,67,79,78,78,69,67,84,93,102,97,98,50,98,51,50,102,48,100,49,55,56,99,48,54,55,48,56,53,55,97,100,54,53,102,54,54,98,48,100,51,50,57,49,57,51,100,55,54,91,47,67,79,78,78,69,67,84,93
[SENDING] finished.
[RECEIVING] Scanner hasNext
[RECEIVING] Exit While loop : [OK1OK2]
Log: **[RECEIVING] Exit While loop :** shall be `[OK1, OK2]`
Server send messagesin every second twice
I just checked some questions and samples b, unfortunately in my case, no one solved the above issue

Message not received from Android MQTT publish to RabbitMQ

I have set up RabbitMQ, enabled web UI for management, enabled mqtt_plugin and the ports 1883, 8883, 5672, 15672 (Docker). I used Paho MqttClient for Android app I am developing to publish a message to the MQ broker. The connection is fine however, there is no message received as a check on the web UI and CLI.
Connection Page:
Channel Page:
Exchange Page:
Queues Page:
Below is the code I'm working on.
private static final String CONNECTION_URL = "tcp://my-app.com:1883";
private static final String USERNAME = "test_user";
private static final String PASSWORD = "test_pass";
private static final String EXCHANGE = "TestExchange";
private static final String QUEUE = "TestQueue";
private static final String TOPIC = "TestTopic";
// executed onCreate
private void initializeMQ() {
Log.d(TAG, "==== STARTING MQTT CONNECTION ====");
String clientId = "Skwamiyou";
client = new MqttAndroidClient(this, CONNECTION_URL, clientId);
MqttConnectOptions options = setConnectionOptions(USERNAME, PASSWORD);
try {
IMqttToken token = client.connect(options);
token.setActionCallback(new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
Log.d(TAG, "Connected");
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
Log.d(TAG, "Failed connection");
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
private static MqttConnectOptions setConnectionOptions(String username, String password) {
MqttConnectOptions options = new MqttConnectOptions();
options.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1);
options.setCleanSession(false);
options.setAutomaticReconnect(true);
options.setUserName(username);
options.setPassword(password.toCharArray());
return options;
}
// this is called on button click publish
public void publishLog() {
Log.d(TAG, "Publishing....");
counter++;
String payload = "Send to My MQ! - " + counter;
try {
MqttMessage message = new MqttMessage(payload.getBytes());
message.setQos(1);
message.setRetained(true);
client.publish(TOPIC, message);
Toast.makeText(this, "MESSAGE SENT! - " + counter, Toast.LENGTH_SHORT).show();
} catch (MqttException e) {
e.printStackTrace();
}
}
I've been looking around for answers and tried reinstalling MQ but still got the same.
Here, is common extention for coonectMq and getting message from it. (MqConnectionExtention.kt)
fun Context.connectMq(publishTopicChannelName: String, onConnectionSuccess: (topic: String?, message: MqttMessage?) -> Unit) {
val mClientId = BuildConfig.CLIENT_ID + System.currentTimeMillis()
val mqttAndroidClient = MqttAndroidClient(this, "tcp://34.212.00.188:1883", mClientId)
Timber.e("ChannelName:$publishTopicChannelName")
mqttAndroidClient.setCallback(object : MqttCallbackExtended {
override fun connectComplete(reconnect: Boolean, serverURI: String) {
if (reconnect) { //addToHistory("Reconnected to : " + serverURI)
Log.e("TAG", "Reconnected to : $serverURI")
// Because Clean Session is true, we need to re-subscribe
try {
mqttAndroidClient.subscribe(publishTopicChannelName, 0, object : IMqttMessageListener {
override fun messageArrived(topic: String?, message: MqttMessage?) {
onConnectionSuccess(topic, message)
}
})
} catch (ex: MqttException) {
System.err.println("Exception whilst subscribing")
ex.printStackTrace()
}
} else { //addToHistory("Connected to: " + serverURI);
Log.e("TAG", "Connected to: $serverURI")
}
}
override fun connectionLost(cause: Throwable) {
Log.e("TAG", "The Connection was lost.")
}
override fun messageArrived(topic: String, message: MqttMessage) {
Log.e("TAG", "Incoming message: " + message.payload.toString())
}
override fun deliveryComplete(token: IMqttDeliveryToken) {}
})
val mqttConnectOptions = setUpConnectionOptions("MQ_CONNECTION_USERNAME", "MQ_CONNECTION_PASSWORD")
mqttConnectOptions.isAutomaticReconnect = true
mqttConnectOptions.isCleanSession = false
try {
mqttAndroidClient.connect(mqttConnectOptions, null, object : IMqttActionListener {
override fun onSuccess(asyncActionToken: IMqttToken) {
val disconnectedBufferOptions = DisconnectedBufferOptions()
disconnectedBufferOptions.isBufferEnabled = true
disconnectedBufferOptions.bufferSize = 100
disconnectedBufferOptions.isPersistBuffer = false
disconnectedBufferOptions.isDeleteOldestMessages = false
mqttAndroidClient.setBufferOpts(disconnectedBufferOptions)
try {
mqttAndroidClient.subscribe(publishTopicChannelName, 0, object : IMqttMessageListener {
override fun messageArrived(topic: String?, message: MqttMessage?) {
onConnectionSuccess(topic, message)
}
})
} catch (ex: MqttException) {
System.err.println("Exception whilst subscribing")
ex.printStackTrace()
}
}
override fun onFailure(asyncActionToken: IMqttToken, exception: Throwable) { //addToHistory("Failed to connect to: " + serverUri);
}
})
} catch (ex: MqttException) {
ex.printStackTrace()
}
}
private fun setUpConnectionOptions(username: String, password: String): MqttConnectOptions {
val connOpts = MqttConnectOptions()
connOpts.isCleanSession = true
connOpts.userName = username
connOpts.password = password.toCharArray()
return connOpts
}
From Java class I am calling it like below and getting message successfully:
private void subscribeMQForVideo() {
MqConnectionExtentionKt.connectMq(mContext, "mq_video_channel_name", (topic, mqttMessage) -> {
// message Arrived!
Log.e("TAG", "Message Video: " + topic + " : " + new String(mqttMessage.getPayload()));
return null;
});
}
To publish message similar extention I have created with little difference. (MqConnectionPublishExtention.kt)
fun Context.connectMq(onConnectionSuccess: (mqttAndroidClient: MqttAndroidClient?) -> Unit) {
val mClientId = BuildConfig.CLIENT_ID + System.currentTimeMillis()
val mqttAndroidClient = MqttAndroidClient(this, BuildConfig.MQ_SERVER_URI, mClientId)
mqttAndroidClient.setCallback(object : MqttCallbackExtended {
override fun connectComplete(reconnect: Boolean, serverURI: String) {
if (reconnect) { //addToHistory("Reconnected to : " + serverURI)
Log.e("TAG", "Reconnected to : $serverURI")
// Because Clean Session is true, we need to re-subscribe
onConnectionSuccess(mqttAndroidClient)
} else { //addToHistory("Connected to: " + serverURI);
Log.e("TAG", "Connected to: $serverURI")
}
}
override fun connectionLost(cause: Throwable) {
Log.e("TAG", "The Connection was lost.")
}
override fun messageArrived(topic: String, message: MqttMessage) {
Log.e("TAG", "Incoming message: " + message.payload.toString())
}
override fun deliveryComplete(token: IMqttDeliveryToken) {}
})
val mqttConnectOptions = setUpConnectionOptions(BuildConfig.MQ_CONNECTION_USERNAME, BuildConfig.MQ_CONNECTION_PASSWORD)
mqttConnectOptions.isAutomaticReconnect = true
mqttConnectOptions.isCleanSession = false
try {
mqttAndroidClient.connect(mqttConnectOptions, null, object : IMqttActionListener {
override fun onSuccess(asyncActionToken: IMqttToken) {
val disconnectedBufferOptions = DisconnectedBufferOptions()
disconnectedBufferOptions.isBufferEnabled = true
disconnectedBufferOptions.bufferSize = 100
disconnectedBufferOptions.isPersistBuffer = false
disconnectedBufferOptions.isDeleteOldestMessages = false
mqttAndroidClient.setBufferOpts(disconnectedBufferOptions)
onConnectionSuccess(mqttAndroidClient)
}
override fun onFailure(asyncActionToken: IMqttToken, exception: Throwable) { //addToHistory("Failed to connect to: " + serverUri);
}
})
} catch (ex: MqttException) {
ex.printStackTrace()
}
}
private fun setUpConnectionOptions(username: String, password: String): MqttConnectOptions {
val connOpts = MqttConnectOptions()
connOpts.isCleanSession = true
connOpts.userName = username
connOpts.password = password.toCharArray()
return connOpts
}
Publish message from java class
private void publishExerciseDataToMQChannel() {
MqConnectionPublishExtentionKt.connectMq(mContext, (mqttAndroidClient) -> {
try {
JSONObject jsonObject = new JSONObject();
jsonObject.put("params", mlParams);
jsonObject.put("workoutid", workoutId);
jsonObject.put("userid", model.getUserIdFromPrefs());
jsonObject.put("stream_id", streamDataModel.getStreamId());
MqttMessage message = new MqttMessage();
message.setPayload(jsonObject.toString().getBytes());
mqttAndroidClient.publish("Channel_name", message);
Log.e("TAG", message.getQos() + "");
if (!mqttAndroidClient.isConnected()) {
Log.e("TAG", mqttAndroidClient.getBufferedMessageCount() + " messages in buffer.");
}
} catch (MqttException e) {
System.err.println("Error Publishing: " + e.getMessage());
e.printStackTrace();
} catch (JSONException e) {
System.err.println("Error Publishing: " + e.getMessage());
e.printStackTrace();
}
return null;
});
}

Unable to read data via bluetooth successfully

I'm trying to create an App which can receive data and send data to the microcontroller (ESP32). But for some reason, I'm unable to receive data from microcontroller successfully.
The app is written in Kotlin, and I already tried some examples mentioned on StackOverflow, but none of them actually works on my code.
I can successfully send data to the microcontroller via Bluetooth, but I can't receive data from Bluetooth. (The method I used in the microcontroller is just simply "ESP_BT.println("Check");"
In the code snippet, the function relates to my receiving data is called "receiveBluetooth"
class ControlActivity: AppCompatActivity() {
companion object {
val myUUID: UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
var myBluetoothSocket: BluetoothSocket? = null
lateinit var myProgress: ProgressDialog
lateinit var myBluetoothAdapter: BluetoothAdapter
var myIsConnected: Boolean = false
lateinit var myAddress: String
val mmInStream: InputStream? = null
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.control_layout)
myAddress = intent.getStringExtra(SelectDeviceActivity.EXTRA_ADDRESS)
ConnectToDevice(this).execute()
val btnShow = findViewById<Button>(R.id.btnShow)
var inputRPM: String
//Read in value and store it as String
btnShow.setOnClickListener{
inputRPM = receiveInput()
sendCommand(inputRPM)
}
//Read RPM from microcontroller (bluetooth)
val showCountTextView = findViewById<TextView>(R.id.textView)
btnRefresh.setOnClickListener {
//showCountTextView.text = receiveBluetooth()
receiveBluetooth(showCountTextView)
}
control_disconnect.setOnClickListener{
disconnect()
}
}
private fun receiveInput(): String {
val input = findViewById<EditText>(R.id.editText)
return input.text.toString()
}
private fun sendCommand(input: String) {
if (myBluetoothSocket != null) {
try{
myBluetoothSocket!!.outputStream.write(input.toByteArray())
} catch (e: IOException) {
e.printStackTrace()
}
}
}
private fun receiveBluetooth(input: TextView) {
val buffer = ByteArray(256)
val bytes:Int
var tmpIn: InputStream? = null
if (myBluetoothSocket != null) {
try {
tmpIn = myBluetoothSocket!!.inputStream
val mmInStream = DataInputStream(tmpIn)
bytes = mmInStream.read(buffer)
val readMessage = String(buffer, 0, bytes)
input.text = readMessage
//input.text="123"
} catch (e:IOException) {
e.printStackTrace()
}
}
}
private fun disconnect() {
if (myBluetoothSocket != null) {
try {
myBluetoothSocket!!.close()
myBluetoothSocket = null
myIsConnected = false
} catch (e: IOException) {
e.printStackTrace()
}
}
finish()
}
private class ConnectToDevice(c: Context) : AsyncTask<Void, Void, String> () {
private var connectSuccess: Boolean = true
private val context: Context
init {
this.context = c
}
override fun onPreExecute() {
super.onPreExecute()
myProgress = ProgressDialog.show(context, "Connecting", "Please wait")
}
override fun doInBackground(vararg params: Void?): String? {
try {
if (myBluetoothSocket == null || !myIsConnected) {
myBluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
val device: BluetoothDevice = myBluetoothAdapter.getRemoteDevice(myAddress)
myBluetoothSocket = device.createInsecureRfcommSocketToServiceRecord(myUUID)
BluetoothAdapter.getDefaultAdapter().cancelDiscovery()
myBluetoothSocket!!.connect()
}
} catch (e: IOException) {
connectSuccess = false
e.printStackTrace()
}
//Needs be fixed
return null
}
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
if (!connectSuccess) {
Log.i("data", "couldn't connect")
} else {
myIsConnected = true
}
myProgress.dismiss()
}
}
}
I expect the text will show exactly "Check", but instead, my text will only show the initial value that I assigned.
Maybe you should use a library. For me works fine RxAndroidBle library:
Gradle:
implementation "com.polidea.rxandroidble2:rxandroidble:1.8.1"
Implementation:
In my project with Android Java and ESP32 too, I read some characteristics or values with simple implementations, for example:
public void setupNotification() {
if (isConnected()) {
final Disposable disposable = connectionObservable
.flatMap(rxBleConnection -> rxBleConnection.setupNotification(charactSensorDataUuid))
.doOnNext(notificationObservable -> { notificationHasBeenSetUp(); })
.flatMap(notificationObservable -> notificationObservable)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onNotificationReceived, this::onNotificationSetupFailure);
compositeDisposable.add(disposable);
}
}
public void readSensorConfig(){
if (isConnected()) {
final Disposable disposable = connectionObservable
.firstOrError()
.flatMap(rxBleConnection -> rxBleConnection.readCharacteristic(charactConfigUuid))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onSensorConfigRead, this::onReadFailure);
compositeDisposable.add(disposable);
}
}
public void readSensorData(){
if (isConnected()) {
final Disposable disposable = connectionObservable
.firstOrError()
.flatMap(rxBleConnection -> rxBleConnection.readCharacteristic(charactSensorDataUuid))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onSensorDataRead, this::onReadFailure);
compositeDisposable.add(disposable);
}
}
The complete Java implementation is here:
https://github.com/kike-canaries/android-hpma115s0/blob/master/app/src/main/java/hpsaturn/pollutionreporter/common/BLEHandler.java
The migration to Kotlin should be simple, also on this library the main target is Bluetooth BLE, and they have many samples on Kotlin

how to set color of RecyclerView.ViewHolder in BroadcastReceiver.onReceive?

i am creating an app that scans for and pairs to Bluetooth devices. i am displaying the devices in a RecyclerView and indicating bond state by coloring the ViewHolder for that device. my problem is that the color of the ViewHolder is only changed after scanning for devices again and i want it to immediately update the color on pair or unpair. i am attempting to do this through use of a broadcast receiver but i am unable to get a reference to the correct ViewHolder. how can i achieve this? i am including my code below for my RecyclerView.Adapter and my BluetoothUtils file containing the broadcast receiver. thanks in advance. my adapter:
class DeviceAdapter(val mContext : Context) : RecyclerView.Adapter<DeviceAdapter.DeviceHolder>() {
companion object {
val TAG = "Device Adapter"
fun DeviceHolder.setColor(bonded: Boolean):Unit{
val background = if (bonded)Color.CYAN else Color.TRANSPARENT
this.itemView.setBackgroundColor(background)
}
}
val mDevices = ArrayList<BluetoothDevice>()
fun updateItems(list: ArrayList<BluetoothDevice>) {
mDevices.clear()
mDevices.addAll(list)
Log.d(TAG, "updating items : $mDevices")
notifyDataSetChanged()
}
fun ViewGroup.inflate(#LayoutRes res: Int, attachToRoot: Boolean = false): View {
return LayoutInflater.from(mContext).inflate(res, this, attachToRoot)
}
override fun onBindViewHolder(holder: DeviceHolder, position: Int) {
Log.d(TAG, "onBindViewHolder called!")
holder.bindItems(mDevices.get(position))
if (mDevices.get(position).bondState==BluetoothDevice.BOND_BONDED) {
holder.itemView.setBackgroundColor(CYAN)
} else {
holder.itemView.setBackgroundColor(Color.TRANSPARENT)
}
}
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): DeviceAdapter.DeviceHolder {
Log.d(TAG, "onCreateViewHolder called!")
val v = parent!!.inflate(R.layout.device_item, false)
return DeviceHolder(v)
}
override fun getItemCount(): Int {
return mDevices.size
}
inner class DeviceHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val nameView = itemView.findViewById(R.id.nameView) as TextView
val addrView = itemView.findViewById(R.id.addressView) as TextView
var dialog: AlertDialog? = null;
fun bindItems(btDevice: BluetoothDevice) {
Log.d(TAG, "holder created!")
nameView.text = btDevice.name ?: "Unknown"
addrView.text = btDevice.address
itemView.setOnClickListener {
dialog = AlertDialog.Builder(it.context)
.setTitle("Options")
.setView(R.layout.options_dialog_layout)
.setNegativeButton("Cancel", DialogInterface.OnClickListener { _, which -> })
.create()
dialog!!.show()
val ops = listOf(
dialog!!.findViewById(R.id.statOp),
dialog!!.findViewById(R.id.pairOp),
dialog!!.findViewById(R.id.connectOp),
dialog!!.findViewById(R.id.sendOp),
dialog!!.findViewById(R.id.unPairOp)
)
ops.forEach { it.setOnClickListener {
Toast.makeText(it.context, it.id.toString(), Toast.LENGTH_SHORT).show()
when(it.id){
R.id.statOp -> {}
R.id.connectOp -> {
Log.d(TAG, "connectOp reached")
BluetoothReflection.connectDevice(btDevice)
dialog!!.dismiss()
}// BluetoothUtils.connect(BluetoothAdapter.getDefaultAdapter(), btDevice)
R.id.pairOp -> {
Log.d(TAG, "pairOp reached")
BluetoothUtils.startPair(BluetoothAdapter.getDefaultAdapter(), btDevice)
if (btDevice.bondState==BluetoothDevice.BOND_BONDED){
this#DeviceHolder.itemView.setBackgroundColor(CYAN) //doesn't work
}
Log.d(TAG, "start pair complete")
dialog!!.dismiss()
}//
R.id.unPairOp -> {//no executable code found here
Log.d(TAG, "unPairOp reached")
BluetoothUtils.unPair(btDevice)
if (btDevice.bondState==BluetoothDevice.BOND_NONE){
this#DeviceHolder.itemView.setBackgroundColor(Color.TRANSPARENT) //doesn't work
}
Log.d(TAG, "unpair complete")
dialog!!.dismiss()
}
R.id.sendOp -> {}
}
} }
}
}
}
}
and my BluetoothUtils:
class BluetoothUtils {
companion object {
var listener: ListenThread? = null
val _UUID = UUID.fromString("a0e7e4c7-0e4e-43b7-9d18-659192512164")
val TAG = "BluetoothUtils"
val receiver = MainBTStatusReceiver()
fun initPairingServer(adapter: BluetoothAdapter){
var mmServerSocket: BluetoothServerSocket?
try {
var tmp = adapter.listenUsingRfcommWithServiceRecord(TAG, _UUID)
mmServerSocket = tmp
listener = ListenThread(mmServerSocket)
listener!!.start()
}catch (ioe: IOException){
Log.e(TAG, "Error initializing Bluetooth", ioe)
}
}
fun cancelListener() = listener!!.cancel()
fun connect(adapter: BluetoothAdapter, device: BluetoothDevice){
var btSocket: BluetoothSocket?
try {
adapter.cancelDiscovery()
btSocket = device.createRfcommSocketToServiceRecord(_UUID)
PairingThread(btSocket).start()
}catch (ioe: IOException){
Log.e(TAG, "error connecting", ioe)
}
}
fun startPair(adapter: BluetoothAdapter, device: BluetoothDevice): Unit{
adapter.cancelDiscovery()
Log.d(TAG, device.bondState.toString())
device.createBond()
}
fun unPair(device: BluetoothDevice): Any = device::class.java.getMethod("removeBond").invoke(device)
}
}
class ListenThread(val btServSock: BluetoothServerSocket) : Thread(){
companion object {
val TAG = "ListenThread"
}
var btSocket: BluetoothSocket? = null
override fun run() {
super.run()
while (true){
try {
Log.d(TAG, "listening . . . ")
btSocket = btServSock.accept()
}catch (ioe: IOException){
Log.e(TAG, "Error", ioe) // SHOULD HANDLE FAILURE OF LISTENER INSTANTIATION
break
}
//manage connection here
//with either BluetoothUtils function
//or BluetoothSocket extension
}
}
fun cancel() = btServSock.close()
}
class PairingThread(val btSocket: BluetoothSocket) : Thread(){
companion object {
val TAG = "Pairing Thread"
}
override fun run() {
super.run()
try {
Log.d(TAG, "attempting to connect")
btSocket.connect()
}catch (ioe: IOException){
Log.e(TAG, "error connecting", ioe)
btSocket.close()
}
}
}
class MainBTStatusReceiver(): BroadcastReceiver(){
val TAG = "MainBTStatusReceiver"
var mAdapter: DeviceAdapter? = null
fun setAdapter(adapter: DeviceAdapter){
mAdapter = adapter
}
override fun onReceive(context: Context?, intent: Intent) {
val action = intent.action
val devExtra = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE) as BluetoothDevice
when(action){
BluetoothDevice.ACTION_BOND_STATE_CHANGED -> {
val device = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
when(device.bondState){
BluetoothDevice.BOND_BONDED -> {
Log.d(TAG, "BONDED")
}
BluetoothDevice.BOND_BONDING -> {Log.d(TAG, "BONDING")}
BluetoothDevice.BOND_NONE -> {Log.d(TAG, "NONE")}
}
}
}
}
Add boolean or int to BluetoothDevice model for managing view.
For example,
BluetoothDevice: added isOn state. (Sorry, it's Java)
class BluetoothDevice {
boolean isOn;
public boolean isOn() {
return isOn;
}
public void setOn(boolean isOn) {
this.isOn = isOn;
}
}
DeviceHolder: changed color of view
fun bindItems(btDevice: BluetoothDevice) {
stateView.textColor = btDevice.isOn() ? Color.RED : Color.GREEN
}
DeviceAdapter: added getItems
fun getItems() {
return mDevices
}
If you want to change isOn state, change model and notify it.
adapter.getItems().get(i).setOn(true);
adapter.notifyDataSetChanged();
I like the above answer as well but the way that i got this done was to pass the BroadcastReceiver into the DeviceAdapter :
class DeviceAdapter(val mContext:Context, val mReceiver:MainBTStatusReceiver) : RecyclerView.Adapter<DeviceAdapter.DeviceHolder>()
and then made a ViewHolder a member of the BroadcastReceiver and a setter function for the ViewHolder named setFocus. before calling any functions from the BluetoothUtils class i called the setFocus function and then the broadcast receiver modifies the color of the view that focus is currently set too. I do have some concern that this might not be reliable as the most accurate method to modify the correct ViewHolder every time. if you see any problem with this please comment to let me know.
my updated BroadcastReceiver:
class MainBTStatusReceiver(): BroadcastReceiver() {
val TAG = "MainBTStatusReceiver"
var holder: DeviceAdapter.DeviceHolder? = null
fun setFocus(holder: DeviceAdapter.DeviceHolder) {
this.holder = holder
}
override fun onReceive(context: Context?, intent: Intent) {
val action = intent.action
when (action) {
BluetoothDevice.ACTION_BOND_STATE_CHANGED -> {
val device = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
when (device.bondState) {
BluetoothDevice.BOND_BONDED -> {
holder!!.itemView.setBackgroundColor(Color.CYAN)
Log.d(TAG, "BONDED")
}
BluetoothDevice.BOND_BONDING -> {
Log.d(TAG, "BONDING")
}
BluetoothDevice.BOND_NONE -> {
holder!!.itemView.setBackgroundColor(Color.TRANSPARENT)
Log.d(TAG, "NONE")
}
}
}
}
}
}
and the two statements in my when expression that call setFocus():
R.id.pairOp -> {
Log.d(TAG, "pairOp reached")
mReceiver.setFocus(this#DeviceHolder)
BluetoothUtils.startPair(BluetoothAdapter.getDefaultAdapter(), btDevice)
Log.d(TAG, "start pair complete")
dialog!!.dismiss()
}
R.id.unPairOp -> {
Log.d(TAG, "unPairOp reached")
mReceiver.setFocus(this#DeviceHolder)
BluetoothUtils.unPair(btDevice)
Log.d(TAG, "unpair complete")
dialog!!.dismiss()
}

Categories

Resources