Kotlin: Pass Bluetooth data to another Activity - android

I am making an android app that monitors sensors from my Arduino Mega 2560 and using Bluetooth HC-05 Module
to connect the app and the Arduino. Right now I have the bluetooth connection and write done but I am stuck on read, I tried calling readBluetoothData in another activity but it freezes the activity and crashes, I would like to know how to pass "readMessage" to another Activity so I can display the sensor values and to stop it from crashing. Really would like to ask advice on this since I am relatively new to both kotlin and Bluetooth coding.
private fun readBluetoothData() {
val bluetoothSocketInputStream = m_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)
} catch (e: IOException) {
e.printStackTrace()
break
}
}
}

can use aplication class it is upstairs then activity
class MainApp: Application() {
you bluetooth conection object
}
manifest:
<application
android:name=".MainApp"
in you activity
context?.applicationContext as MainApp).(you bluetooth conection object)
or use single activity app and fragments

I think the better solution is using Android service for bluetooth work and exchange data with app using broadcast receiver or something similar
Don't forget to use another thread or process for preventing app to stuck in a loop

Related

Can't figure out how to use writeCharacteristic function (BLE) in Kotlin

I'm quite new to Kotlin and to Android development, but I have got a proper connection between my phone and a HM-10 bluetooth module. This bluetooth module is a BLE device.
I only want to send a simple "1" to the Bluetooth module, and I am attempting to do so with this function.
fun writeCharacteristic(){
service = mBluetoothGatt!!.getService(
UUID.fromString("00001800-0000-1000-8000-00805f9b34fb"))
val characteristic : BluetoothGattCharacteristic =
service.getCharacteristic(UUID.fromString("00002a03-0000-1000-8000-00805f9b34fb"))
mBluetoothGatt?.let { gatt ->
characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
characteristic.value = "1".toByteArray()
gatt.writeCharacteristic(characteristic)
} ?: error("No established connection")
}
I apologize if there is something very obvious I am missing, but I have done my best to try and stick to the official Android documentation about this and they do a poor job of actually explaining how this specific protocol works. I tried the same thing a few months earlier with Bluetooth classic and it worked like a breeze :D
My actual issue is that this doesn't actually do anything. I get a null pointer exception on the bluetooth adapter.
EDIT (as per request by user emil):
class WakeupReceiver : BroadcastReceiver() {
#RequiresApi(Build.VERSION_CODES.M)
override fun onReceive(context: Context?, intent: Intent?) {
BluetoothLeService().writeCharacteristic()
Log.i("Confirmation", "Message sent")
}
}
This is how I call the function. Right now I can't get a reference to the specific error I get, but it is a NullPointerException.

Getting duplicate BLE notifications on Android

I'm developing a frame exchange sequence between an nRF52840 and an Android smartphone using the BLE protocol.
The first time I connect, everything works fine.
I activate the listening of BLE notifications by the Android smartphone with this method:
fun enableBleNotificationsOnCentral(currentBluetoothGatt: BluetoothGatt, serviceUUID: UUID, characteristicUUID: UUID) {
getMainDeviceService(currentBluetoothGatt, serviceUUID)?.let { service ->
val notificationConfiguration = service.getCharacteristic(characteristicUUID)
val result = currentBluetoothGatt.setCharacteristicNotification(notificationConfiguration, true)
println(result)
}
}
And I enable sending BLE notifications on the nRF52840 with this method:
fun enableBleNotificationsOnPeripheral(currentBluetoothGatt: BluetoothGatt, serviceUUID: UUID, characteristicUUID: UUID, descriptorUUID: UUID) {
getMainDeviceService(currentBluetoothGatt, serviceUUID)?.let { service ->
val descriptorConfiguration = service.getCharacteristic(characteristicUUID).getDescriptor(
descriptorUUID).apply {
value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
}
val result = currentBluetoothGatt.writeDescriptor(descriptorConfiguration)
println(result)
}
}
These methods are called each time my smartphone is connected to the nRF52840.
But if I disconnect and connect a second time, I receive each of the notifications in duplicate.
In addition, if I disconnect and connect a 3rd time, I receive each notification 3 times, and one more each time I reconnect.
I checked my code on the nRF52840 and it does not duplicate notifications.
Here is the method I call when I request a disconnection:
private fun disconnectFromCurrentDevice() {
currentBluetoothGatt?.disconnect()
BLECallbackManager.currentDevice = null
setUiMode(false)
}
I guess my problem is related to the fact that I don't disable the receipt of BLE notifications by my Android application when I disconnect but I'm not sure. And if that's where the problem comes from, when should I do it in the disconnect method? Can you help me?
I guess you're creating a new BluetoothGatt object for every new connection attempt, but you not destroy the previous one.
Try change disconnect() to close().

How to get my own endpointId with Nearby Connections?

With Nearby Connections, each device has an endpointId, something similar to zkHk.
Getting the endpointId of others is trivial since it is returned by the API when scanning or connecting to other devices.
I must miss something, but I cannot find a way to get my own endpointId (apart implementing a mechanism where a connected peer echoes my id). It can be useful for some protocols where I want to follow what is sent to who.
The only thing I found is getLocalEndpointName but it returns my name, not my id. Even though it seems the C++ version of Nearby have it!
Do you have some ideas for Java/Kotlin? I specifically seek to get the endpointId, and not use alternatives like using a kind of GUID in the localendpoint name as a replacement.
Edit: Some example of usage
1) For instance, it can be interesting to implement some network mesh protocols. Several devices are interconnected making a global network, and each device add its endpointId in the incoming payload before sending it again, so others can check if they should send the payload to a device that already has it.
2) I may also want to specifically send a packet from device A to C through B acting as a relay, and add some "from: A" and "to: C" field in the payload so the network would know how to route the data and avoid some retransmission cycles. It is simpler to do that with endpointId since each device has a list of endpointId to which it is connected.
3) It can also be interesting for debug purpose. If I do some tests with a phone connected to several others (e.g. star network), it is easier to know from which phone a new piece of data is coming, all the more if I want to use name for another purpose.
Note: all of that could be done differently (e.g. use some unique identifier for the "name" of the devices and check that instead of the endpointId) but it seems a little cumbersome. All the more since endpointId guarantee a kind of unicity, whereas I must enforce it for the name. Moreover there isn't lots of information I can have on another device before exchanging data (only endpointId and name), so I feel I remove my last metadata slot if I use name as a substitute for endpointId.
As of today, you can't get your own endpoint id. We didn't see a reason you'd need it. Can you give a more detailed example of an algorithm where you need to know your own id?
i think you want to get your endpointId and sent its to other devices to know you again ?
if yes
let's think like that :
other devices will get your EndpointID and save it every time you connect to them
1)you have an Arrylist<EndPointObject> listOfUsers where EndPointObject it's an Object contain informations about Connected Endpoint Device (you create this class).
we w'ill use this Arry list to save recieved Endpoint informations
2)you need to make EndPointObject class Serializable by implements Serializable,you are doing that to make it able to be converted to Byte[] and send it in payload
public class EndPointObject implements Serializable
{
String endpointId ;
.
.
.
}
3)this is the Converting class add it to your project
public class SerializeHelperForPayLoad {
public static byte[] serialize(Object object) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
// transform object to stream and then to a byte array
objectOutputStream.writeObject(object);
objectOutputStream.flush();
objectOutputStream.close();
return byteArrayOutputStream.toByteArray();
}
public static Object deserialize(byte[] bytes) throws IOException, ClassNotFoundException{
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
return objectInputStream.readObject();
}
}
4) now the strategy is every time you connect to an endpoint Device you will exchange yours EndpointObject informations,so in payloadcallback
PayloadCallback mPayloadCallback =
new PayloadCallback() {
#Override
public void onPayloadReceived(String endpointId, Payload payload) {
if (payload.getType() == Payload.Type.BYTES) {
try{
onDataReceived(endpointId, SerializeHelperForPayLoad.deserialize(payload.asBytes()));
} catch (IOException | ClassNotFoundException e) { e.getMessage(); }
}
}
// onData recieved void
void onDataReceived(String endpointId, Object object) {
// do something with your Object
EndPointObject recieved_user_info = new EndPointObject();
if (object.getClass() == EndPointObject.class){
//casting
recieved_user_info = (EndPointObject) object;
//now add his end pointid to his information
recieved_user_info.setEndpointId(endpointId);
listOfUsers.add(recieved_user_info);
}
}
i'm very new in nearby technology ,but i hope that's helpful ,
by this way you can ask other end endpoint to send you your own endpointid every time

USB interrupt transfer lost data

Since few days I am dealing with confusing USB problem. My Android(4.2) application should have connect with USB device which transmit/receive data using USB HID interrupt protocol. Sending data from Android to device works perfectly but I have got plenty of problems with receiving proper data. Main application based on 3 tasks: main task (UI), USB receive task and USB transmit task. USB's tasks have separate queue to insert received data (receiver) or obtain data to send (sender) which comes from main task. I try to sniff data that comes over USB (using android virtual machine and Windows USB analyzer) and everything looks fine but Android receives only zeros (sometimes receives another expected values).
Here is my reading and writting code - both works in separate Runnable instance:
public boolean writeRawData(UsbDeviceConnection connection, UsbRequest request, ByteBuffer buffer, int bufsize)
{
if (request.queue(buffer, bufsize) == true)
{
return true;
}
return false;
}
public ByteBuffer readRawData(UsbDeviceConnection connection, UsbRequest request, ByteBuffer buffer, int bufsize)
{
if (request.queue(buffer, bufsize) == true)
{
if (connection.requestWait() == request)
{
//deal with received data -> almost always wrong!
}
}
return null;
}

How to connect to Bluetooth SPP using dot42?

After reading the dot42 comments and trolling Java examples I managed to setup a Bluetooth connection but fail to open the connection. I cannot determine the problem. I followed the docs step by step.
My target device is a HTC Explorer running on 2.3 Gingerbread. Here is my code.
//Target 2.3 (Gingerbread)
[assembly: Application("dot42Application1")]
[assembly: UsesPermission(Android.Manifest.Permission.BLUETOOTH)]
[assembly: UsesPermission(Android.Manifest.Permission.BLUETOOTH_ADMIN)]
namespace dot42Application1
{
[Activity]
public class MainActivity : Activity
{
private TextView txStatus;
protected override void OnCreate(Bundle savedInstance)
{
base.OnCreate(savedInstance);
SetContentView(R.Layouts.MainLayout);
// Find UI controls
txStatus = FindViewById<TextView>(R.Ids.txStatus);
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
var bt = BluetoothAdapter.GetDefaultAdapter();
if (bt != null) //If device has not Bluetooth this will be null
{
if (bt.IsEnabled()) //Is Bluetooth device enabled?
{
var BT_My_Addr = bt.Address; //Get the devices MAC
var BT_Bonded = bt.GetBondedDevices().ToList(); //Get a list of bonded devices- I bonded to a BT2TTL Board earlier.
txStatus.Text = BT_My_Addr + System.Environment.NewLine; //Shows my MAC on screen.
string BT_Remote_Address = string.Empty;
foreach (var BTDevice in BT_Bonded) //Just searchging for string in bonded list
{
if (BTDevice.Name.Contains("linvor"))
{
BT_Remote_Address = BTDevice.Address;
}
}
//Gets remote device
var BT_Remote_Device = bt.GetRemoteDevice(BT_Remote_Address);
//Create a RFCOMM Socket to remote device using popular UUID ofr BT Serial boards
var BTsocket = BT_Remote_Device.CreateInsecureRfcommSocketToServiceRecord(Java.Util.UUID.FromString("00001101-0000-1000-8000-00805F9B34FB"));
//Call anyway to make sure there is no discvoerry in the backgorund. It slows stuff down.
bt.CancelDiscovery();
//Exception here? Dont know why :(
BTsocket.Connect();
//Suppsoed to dump 0 to 99999 to my listening serial device but I never get this far.
var BT_Out = BTsocket.GetOutputStream();
for (int i = 0; i < 99999; i++)
{
BT_Out.Write(Encoding.ASCII.GetBytes(i.ToString()));
}
}
else
{
txStatus.Text = "Bluetooth is disabled :(";
}
}
}
}
And this is what it shows after the socket creation
and the error...
What am I doing wrong? :(
I seem to have solved the problem by analysing various code snippets on the internet. I think the problem was trying to do everything in the OnCreate method. The steps I followed are the following:
Created a button on the main view (MainActivity.xml) and attached a onClick method.
Moved all the code OUT of the OnCreate method. (I think this allows the application to fully initialise.) Created an event handler for the button with two methods.
The two methods are the same as the code I posted in my original question. Just they are separated out and called when the user clicks the button.
findBT() Gets the default adapter. Checks if Bluetooth is enabled if not does the intent filter. Or if it is it will cycle through the bonded list and match a device name and store the BluetoohDevice in a variable. This is another thing that is different from my code. I do not use GetRemoteDevice I just assign the device from the BondedList to my global variable.
openBT() creates the RFCOMM socket (this did not work with unsecure - it threw an exception but using the secure method worked!)
You have to pair to the remote device using the Androids Bluetooth control panel. This code will not scan or connect to devices that are not paired. It will just throw null exceptions.
Also I left the target SDK 2.3.x but I am using the 4.x API.
-Disclosure. I am not a seasoned Android developer and just learning about the life cycle of Java applications in the Android context. I hope this can help other C# developers trying to do the same.

Categories

Resources