I am working on a small app that connects via bluetooth to an Arduino with a bluetooth shield attached. My bluetooth connection is fine and I'm able to send commands from my app to the Arduino. I'm doing this in Kotlin. I'm learning as I go, so I'm misunderstanding something. Which is where I hope someone can point me in the right direction.
You can assume that all the bluetooth connection stuff is working fine(it is).
This is the part of my code that handles the sending of data to the Arduino.
private fun writeDataSendToMothership(outputToBt: String) {
try {
bluetoothSocket.outputStream.write(outputToBt.toByteArray())
Log.i(LOGTAG, "Button clicked, info sent: $outputToBt")
} catch (e: IOException) {
e.printStackTrace()
}
}
button_led_on.setOnClickListener { writeDataSendToMothership("1")}
button_led_off.setOnClickListener { writeDataSendToMothership("0")}
The part i'm having trouble with is receiving data from the Arduino(Mothership) and doing something with it. I cannot figure out what I need to do.
What I am trying to do is show an image in the app depending on what the Arduino sends after a button on the Arduino is pushed.
What I have so far is:
private fun readDataFromMothership(inputFromBt: String) {
try {
bluetoothSocket.inputStream.read(inputFromBt.toByteArray())
Log.i(LOGTAG, "Incoming data from Mothership recieved: $inputFromBt")
} catch (e: IOException) {
e.printStackTrace()
}
}
private fun View.showOrInvisible(imageShow: Boolean) {
visibility = if (imageShow) {
View.VISIBLE
} else {
View.INVISIBLE
}
}
This is where I fall flat.
if (readDataFromMothership()) {
imageView_mothership_button_pushed.showOrInvisible(true)
} else {
imageView_mothership_button_pushed.showOrInvisible(false)
}
I've left out anything from that function call. I've tried many different things, but I'm just not understanding what parameter I need, or am I way off. Am I even in the right neighborhood?
EDIT Other than my lack of general knowledge about programming, I think my hangup has to do with what to do with the "inputFromBt" String. Do I need to use a buffer of some sort. I'm trying/researching/reading up on everything I can. But stalling out.
Here is the code I have in place and currently working in my app:
private fun readBlueToothDataFromMothership(bluetoothSocket: BluetoothSocket) {
Log.i(LOGTAG, Thread.currentThread().name)
val bluetoothSocketInputStream = bluetoothSocket.inputStream
val buffer = ByteArray(1024)
var bytes: Int
//Loop to listen for received bluetooth messages
while (true) {
try {
bytes = bluetoothSocketInputStream.read(buffer)
val readMessage = String(buffer, 0, bytes)
liveData.postValue(readMessage)
} catch (e: IOException) {
e.printStackTrace()
break
}
}
}
// display or don't star image
private fun View.showOrHideImage(imageShow: Boolean) {
visibility = if (imageShow) View.VISIBLE else View.GONE
}
I mentioned in a comment to user frnnd, My main issue was the data being sent from my arduino. I was using println() instead of print() and that newline was messing things up.
Related
I am using Health Connect to read records, like steps and exercises. I use Health Connect in a few different places in Kotlin, and the code generally looks something like:
suspend fun fetchStepData(
healthConnectClient: HealthConnectClient,
viewModel: StepViewViewModel,
): StepViewViewModel {
kotlin.runCatching {
val todayStart = Instant.now().atZone(ZoneId.systemDefault()).toLocalDate().atStartOfDay();
val response: ReadRecordsResponse<StepsRecord>
try {
response = healthConnectClient.readRecords(
ReadRecordsRequest(
StepsRecord::class,
timeRangeFilter = TimeRangeFilter.after(todayStart)
)
)
var steps: Long = 0;
if (response.records.isNotEmpty()) {
for (stepRecord in response.records) {
steps += stepRecord.count
}
}
return viewModel
} catch (e: Exception) {
Log.e("StepUtil", "Unhandled exception, ", e)
}
}
return viewModel
}
I have an update function that is run when focus changes to ensure that the app is in the foreground.
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (hasFocus) {
binding.root.invalidate()
val fragment =
supportFragmentManager.findFragmentById(R.id.nav_host_fragment_activity_main)?.childFragmentManager?.primaryNavigationFragment
if (fragment is MyFragment) {
displayLoadingIndicator(true)
runBlocking {
if (fragment.fetchStepData(this#MainActivity.healthConnectClient, fragment.getViewModel()) != null) {
displayLoadingIndicator(false)
}
}
}
I have a loading indicator present when I am fetching the data.
I use a drawer, and if I wait about 15 seconds and press the drawer button corresponding with MyFragment, the application hangs on the loading indicator, never successfully dismissing it.
I've tried stepping through the application in debug mode, and as I do, I always hang on
response = healthConnectClient.readRecords(
ReadRecordsRequest(
StepsRecord::class,
timeRangeFilter = TimeRangeFilter.after(todayStart)
)
)
in fetchStepData. I did at one point have my application making multiple requests for HealthConnectClient.getOrCreate(context), but I have since consolidated them to one instantiation call. I'm thinking I may be reading the data wrong and maybe I need to use getChanges, or maybe I'm being rate limited. Does anyone have any insight? Thanks in advance!
I have the following code using an. RxAndroidBle Bluetooth Low Energy Connection:
private val connectionDisposable = CompositeDisposable()
private fun writeBle(writeCharacteristicUuid: UUID, command: ByteArray)
if (bleDevice.connectionState == RxBleConnection.RxBleConnectionState.CONNECTED) {
activeConnection
.flatMapSingle {
it.writeCharacteristic(writeCharacteristicUuid, command)
}
.subscribe({
Log.d(
TAG,
"${connectionDisposable.size()} - Command successful: ${it.toHexString()}"
)
})
{ Log.e(TAG, "Error executing command: $it") }
.let { connectionDisposable.add(it) }
} else {
Log.e(TAG, "You are not connected")
}
}
The connectionDisposable is .clear()ed when the connection to the device is closed.
But until then several hundreds, thousands or more disposable will land in the connectionDisposable.
I am not completely clear if this presents a Problem in regard to memory usage, or whether I am missing the right way to execute a lot of write commands (that should not be send simultaneously to the device).
Kotlin How to check if there is something to read from the socket?
Is there an analog of unix select() in kotlin. As in python select.select(). The application must connect to a python server (I have already done this)
And at the request of the user to send data to the server, and at the request of the server to display some data to the user. How do I check if something was sent from the server side?
Just in case, this is how I send data to the server (it is necessary to receive data through the same socket):
private var clientSocket: Socket? = null
override fun doInBackground(vararg params: String?): Int {
if (clientSocket != null) {
var clientSocketOut: OutputStream? = null
try {
clientSocketOut = clientSocket!!.getOutputStream()
} catch (e: IOException) {
e.printStackTrace() }
while (clientSocketOut != null && clientSocket!!.isConnected()) {
if (toSend.size > 0){
for (nowMsg in toSend){
try {
clientSocketOut.write((nowMsg.length + 2).toString().toByteArray(Charsets.UTF_8))
clientSocketOut.flush()
clientSocketOut.write(nowMsg.toByteArray(Charsets.UTF_8))
clientSocketOut.flush()
} catch (e: IOException) {
e.printStackTrace()
}
}
toSend.clear()
}
}
}
P.S. unfortunately, the structure and task of the application is such that I do not know at what point the server will need to send something.
unfortunately, the structure and tasks of the application is such that I do not know at what point the server will need to send something. that's why I can't just receive a message from the server after sending it, because it will block the stream.
Finally I found
the InputStream buffer has a .ready() method
clientSocket = Socket(SERVER_ADDRESS, SERVER_PORT)
clientSocketIn = clientSocket!!.getInputStream()
var tmp = clientSocketIn!!.bufferedReader(Charsets.UTF_8)
if(tmp.ready()){ ...}
I am building an Android Application that helps users to print a receipt. What I've done so far:
Right now, I can find the device/printer(DATECS DP-25) through Bluetooth and connect it to the printer. The connection flow works perfectly. Problem is that my printer doesn't start printing at all with the commands that I've introduced so far.
Probably commands are wrong, or I don't really know exactly what is the problem. I tried a lot of apps but none of them with success in printing. Just one app from PlayStore it's working but no access to the code of that app.
Code for the printing flow:
Initializing variables:
private suspend fun init(): Boolean {
return withContext(Dispatchers.IO) {
return#withContext try {
uuidSting = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb")
bluetoothSocket = bluetoothDevice.createRfcommSocketToServiceRecord(uuidSting)
bluetoothSocket.connect()
outputStream = bluetoothSocket.outputStream
inputStream = bluetoothSocket.inputStream
printWriter = PrintWriter(outputStream.bufferedWriter(Charsets.ISO_8859_1))
true
} catch (e: Exception) {
println(e.stackTraceToString())
false
}
}
}
Print function:
fun print(text: String){
printerController.apply {
initPrinter()
patchText(text)
nextLine()
nextLine()
}
}
#Throws(IOException::class)
fun initPrinter() {
printWriter.write(0x1B)
printWriter.write(0x40)
printWriter.flush()
}
#Throws(IOException::class)
fun patchText(text: String) {
printWriter.println(text)
printWriter.flush()
}
#Throws(IOException::class)
fun nextLine() {
printWriter.write("\n")
printWriter.flush()
}
I would be more than happy if anybody can help me with some advice or directions.
I am new to Android NFC and developing NFC application in android. My idea is Device A need to send a plain text to Device B. Is it possible in Android NFC?
I just tried with Tag Dispatcher (enableForegroundDispatch , disableForegroundDispatch) on both Reader and Writer.
My Reader side code is :
nfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFilters, techList)
override fun onNewIntent(intent: Intent?) {
intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)?.also { rawMessages ->
val messages: List<NdefMessage> = rawMessages.map { it as NdefMessage }
for (message in messages) {
for (record in message.records) {
println(" ${record.toString()}")
}
}
}
}
My Writer side code is:
nfcAdapter.enableForegroundDispatch(
this, pendingIntent, intentFilters, techList)
override fun onNewIntent(intent: Intent?) {
if (action.equals(NfcAdapter.EXTRA_TAG)) {
val tagFromIntent = intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)
if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)
|| NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)
) {
println("testing=============== tag discovered ")
writeNdefMessage(tagFromIntent!!, "This is my first app")
}
}}
private fun writeNdefMessage(tag: Tag, message: String) {
val record: NdefRecord = newTextRecord(message, Locale.ENGLISH, true)!!
val ndefMessage = NdefMessage(arrayOf(record))
try {
if (isExist(tag.techList, NdefFormatable::class.java.name)) {
val ndefFormatable = NdefFormatable.get(tag)
try {
if (!ndefFormatable.isConnected) {
ndefFormatable.connect()
}
ndefFormatable.format(ndefMessage)
} finally {
ndefFormatable.close()
}
} else if (isExist(tag.techList, Ndef::class.java.name)) {
val ndef = Ndef.get(tag)
try {
if (!ndef.isConnected) {
ndef.connect()
}
if (ndef.isWritable) {
ndef.writeNdefMessage(ndefMessage)
}
} finally {
ndef.close()
}
}
} catch (e: FormatException) {
println("Format failed exception")
} catch (e: IOException) {
println("")
}
}
Application is launched when I scan the Tag (via AndroidManifest.xml details). But I am not able to send plain text via NFC. I don't know what I did wrong. I don't know whether the approach is right or wrong. Please help me to proceed this.
Thanks in advance.
So in Android peer to peer NFC (Device to Device) also called Android Beam has been deprecated as of API 29
See https://developer.android.com/reference/android/nfc/NfcAdapter#setNdefPushMessage(android.nfc.NdefMessage,%20android.app.Activity,%20android.app.Activity...)
You are using the wrong methods to use Android Beam in older Android Versions.
See https://developer.android.com/guide/topics/connectivity/nfc/nfc#p2p for more details of actually how to use it. (You are using methods for writing to a NFC card not another Device)
Note Peer to Peer via NFC is Android only, iOS does not support it and it is depreciated in favour of Bluetooth/Wifi Direct
Note that it is still possible to have one Android Device use Host Card Emulation to Emulate a Type 4 NFC card with an NDEF messages on it but this is quite complicated to achieve.
Update:
Link to Host Card Emulation https://developer.android.com/guide/topics/connectivity/nfc/hce and Type 4 card spec http://apps4android.org/nfc-specifications/NFCForum-TS-Type-4-Tag_2.0.pdf