android Kotlin socket scanner.hasNext() has No response - android
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
Related
How to get variables out from a asynctask + try & catch block in kotlin (android studio)?
In these below two functions I am getting referrerUrl and addId. I want both of them to be fetched in onCreate but don't know how because it is in try & catch block also the getGaid() function is not running without AsyncTask. fun getreferrUrl() { //to install referrer client val referrerClient = InstallReferrerClient.newBuilder(this).build() referrerClient.startConnection(object : InstallReferrerStateListener { override fun onInstallReferrerSetupFinished(responseCode: Int) { when (responseCode) { InstallReferrerResponse.OK -> { // Connection established. try { val response: ReferrerDetails = referrerClient.installReferrer val referrerUrl = response.installReferrer // here we need referrerUrl out from this fuction } catch (e: RemoteException) { e.printStackTrace() } } // fun getGaid() { AsyncTask.execute { try { val adInfo = AdvertisingIdClient.getAdvertisingIdInfo(this) val myId: String = if (adInfo != null) adInfo.id else null.toString() //here we need myId out from this fuction } catch (e: java.lang.Exception) {...} } } In onCreate we need both of those strings. // In onCreate val url = "http://instbng.com?device_id=$device_id& &kd_id=$kd_id&ref=$referrerUrl&gaid=$myId" loadUrl(url)
Without coroutines, you can put the results in properties, and create a function that uses both properties and call it from both callbacks. I renamed your get... functions to fetch... since they are asynchronous. The word get in a function name implies they are synchronous. private var referrerUrl: String? = null private var myId: String? = null override fun onCreate(bundle: SavedInstanceState?) { super.onCreate(bundle) //... fetchReferrerUrl() fetchGaId() } // proceeds with workflow if referrerUrl and myId are both available private fun proceedIfReady() { val referrer = referrerUrl ?: return val id = myId ?: return val url = "http://instbng.com?device_id=$device_id&kd_id=$kd_id&ref=$referrer&gaid=$idd" loadUrl(url) } fun fetchReferrerUrl() { val referrerClient = InstallReferrerClient.newBuilder(this).build() referrerClient.startConnection(object : InstallReferrerStateListener { override fun onInstallReferrerSetupFinished(responseCode: Int) { when (responseCode) { InstallReferrerResponse.OK -> { // Connection established. try { val response: ReferrerDetails = referrerClient.installReferrer referrerUrl = response.installReferrer proceedIfReady() } catch (e: RemoteException) { e.printStackTrace() } } } } //... handle closed connection callback } } private fun fetchGaId() { AsyncTask.execute { try { val adInfo = AdvertisingIdClient.getAdvertisingIdInfo(this) runOnUiThread { // in a Fragment use view?.post myId = if (adInfo != null) adInfo.id else null.toString() proceedIfReady() } } catch (e: java.lang.Exception) {...} } }
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) } }
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
Bt Socket closed exception
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.