1

after successfully converting a Byte array into a Hex string using:

  private ScanCallback leScanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            ScanRecord Scanned = result.getScanRecord();
            String address = btAdapter.getAddress();

            byte[] packetData = Scanned.getBytes();
            StringBuilder R = new StringBuilder();
                for(byte hex : packetData){
                    R.append(String.format("%02X", hex));
                }

I would like to now have the ability to read each bit of the hex stream separately as numerical values to allow basic logic to be performed on the values for my BLE scanner App.

I have read a lot of websites and my initial idea was to use an array within a for loop and read in each character of the hex string. But i am new to java and have not been successful in doing this yet!

Any ideas on how i can efficiently do this would be great.

Edit:

The initial output used getBytes() and gave a byte array output such as "[B@" , followed by a few numbers and letters. This value was not the format i required and i needed the 62 bit hex stream, which is now successfully printing thanks to the for loop!

I now want to seperate the 62 bit packet length hex data into separately as there current values. NOT converting them back to the BYTE array.

Hope this clarifies the requirements more

EDIT: full code so far

package com.example.joelwasserman.androidbletutorial;


import android.Manifest;
import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanRecord;
import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.ParcelUuid;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;

import java.util.List;
import java.util.UUID;
import java.util.Vector;

public class MainActivity extends AppCompatActivity {

    BluetoothManager btManager;
    BluetoothAdapter btAdapter;
    BluetoothLeScanner btScanner;
    Button startScanningButton;
    Button stopScanningButton;
    TextView peripheralTextView;
    private final static int REQUEST_ENABLE_BT = 1;
    private static final int PERMISSION_REQUEST_COARSE_LOCATION = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        peripheralTextView = (TextView) findViewById(R.id.PeripheralTextView);
        peripheralTextView.setMovementMethod(new ScrollingMovementMethod());

        startScanningButton = (Button) findViewById(R.id.StartScanButton);
        startScanningButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                startScanning();
            }
        });

        stopScanningButton = (Button) findViewById(R.id.StopScanButton);
        stopScanningButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                stopScanning();
            }
        });
        stopScanningButton.setVisibility(View.INVISIBLE);

        btManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
        btAdapter = btManager.getAdapter();
        btScanner = btAdapter.getBluetoothLeScanner();


        if (btAdapter != null && !btAdapter.isEnabled()) {
            Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableIntent,REQUEST_ENABLE_BT);
        }

        // Make sure we have access coarse location enabled, if not, prompt the user to enable it
        if (this.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle("This app needs location access");
            builder.setMessage("Please grant location access so this app can detect peripherals.");
            builder.setPositiveButton(android.R.string.ok, null);
            builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
                @Override
                public void onDismiss(DialogInterface dialog) {
                    requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION);
                }
            });
            builder.show();
        }
    }

    // Device scan callback.
    private ScanCallback leScanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            ScanRecord Scanned = result.getScanRecord();
            String address = btAdapter.getAddress();

            byte[] packetData = Scanned.getBytes();
            
            /*StringBuilder R = new StringBuilder();
                for(byte hex : packetData){
                    R.append(String.format("%02X", hex));
                }*/
            int PacketLength = Scanned.getBytes().length;
//            int x = Integer.parseInt(String.valueOf(packetData));
//            String int_val = Integer.toString(x);

            if(new String("SimpleBLEBroadcaster").equals(result.getDevice().getName()))
                peripheralTextView.append("Device Name: " + result.getDevice().getName() + " rssi: " + result.getRssi() +" Packet length: " + PacketLength + " Packet Data: " + "0x" + R  +  "\n");
        }
    };

    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           String permissions[], int[] grantResults) {
        switch (requestCode) {
            case PERMISSION_REQUEST_COARSE_LOCATION: {
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    System.out.println("coarse location permission granted");
                } else {
                    final AlertDialog.Builder builder = new AlertDialog.Builder(this);
                    builder.setTitle("Functionality limited");
                    builder.setMessage("Since location access has not been granted, this app will not be able to discover beacons when in the background.");
                    builder.setPositiveButton(android.R.string.ok, null);
                    builder.setOnDismissListener(new DialogInterface.OnDismissListener() {

                        @Override
                        public void onDismiss(DialogInterface dialog) {
                        }

                    });
                    builder.show();
                }
                return;
            }
        }
    }

    public void startScanning() {
        System.out.println("start scanning");
        peripheralTextView.setText("");
        startScanningButton.setVisibility(View.INVISIBLE);
        stopScanningButton.setVisibility(View.VISIBLE);
        AsyncTask.execute(new Runnable() {
            @Override
            public void run() {
                btScanner.startScan(leScanCallback);
            }
        });
    }

    public void stopScanning() {
        System.out.println("stopping scanning");
        peripheralTextView.append("Stopped Scanning");
        startScanningButton.setVisibility(View.VISIBLE);
        stopScanningButton.setVisibility(View.INVISIBLE);
        AsyncTask.execute(new Runnable() {
            @Override
            public void run() {
                btScanner.stopScan(leScanCallback);
            }
        });
    }

    public static byte[] hexStringToByteArray(String s) throws IOException {
        char[] buf = new char[2];
        int numRead = -1;
        StringReader in = new StringReader(s);
        byte[] bytes = new byte[s.length() / 2];
        int byteIndex = 0;
        while ((numRead = in.read(buf)) > -1) {
            byte b = (byte) Integer.parseInt(String.valueOf(buf), 16);
            bytes[byteIndex++] = b;
        }
        return bytes;
    }
}
GAZZA
  • 47
  • 6
  • I think the [answer to that request](https://stackoverflow.com/a/9354899/6538278) would be useful for you – 0009laH Jul 26 '21 at 11:36
  • The most interesting part of your question is what operations you want to do with your bits since a good implementation (if possible) use word instructions to operate at the same time on all word bits. If not, think that the `i-th` (i=0,1,2,...) bit of a byte `b` is `(b >> i) & 1`. – josejuan Jul 26 '21 at 11:43
  • You're using the hex string for visualisation purposes? If not, and it's for transport, you should probably use Base64 as it's more compact and designed for that purpose – g00se Jul 26 '21 at 11:55
  • @009IaH I have made an edit to the question as my first time writing may not have been as clear! someone made a question edit suggestion which didnt really suit what i wanted as far as i am aware – GAZZA Jul 26 '21 at 12:00
  • @josejuan The reason for the split of digits is mainly for manipulation of the values. I am doing this work for someone's research and have been asked to seperate the hex values for carrying data – GAZZA Jul 26 '21 at 12:06
  • @g00se Currently the hex stream relative to the scanned BLE device is being visualized on the app, however i do want to be able to edit each of the values within the 62 bit packet separately. Would that require the use of Base64? – GAZZA Jul 26 '21 at 12:08

1 Answers1

1

Bearing in mind what I said about transport vs. visualization, what you've got could be handled with:

    public static byte[] hexStringToByteArray(String s) throws IOException {
        char[] buf = new char[2];
        int numRead = -1;
        StringReader in = new StringReader(s);
        byte[] bytes = new byte[s.length() / 2];
        int byteIndex = 0;
        while ((numRead = in.read(buf)) > -1) {
            byte b = (byte) Integer.parseInt(String.valueOf(buf), 16);
            bytes[byteIndex++] = b;
        }
        return bytes;
    }
g00se
  • 3,207
  • 2
  • 5
  • 9
  • Hi, would this work within the scancallback function or would i need to define it somewhere else and then use the variables in my scancallback function? – GAZZA Jul 26 '21 at 13:30
  • I would put it elsewhere. It's generalisable functionality – g00se Jul 26 '21 at 13:33
  • Okay, how do i call this so that it prints into my app with the rest of my information? ( i will add my code to the question now) – GAZZA Jul 26 '21 at 13:41
  • I'm confused. You now seem to have the exact opposite source in *private ScanCallback leScanCallback = new ScanCallback()* (namely a `byte[]` and not `String`). IF that's where I'm meant to be looking. Is it? – g00se Jul 26 '21 at 13:47
  • I added your code to the bottom of my code! I assume I would want to call that function in using my packetData variable to allow the conversion to occur and then to be printed onto the APP? – GAZZA Jul 26 '21 at 13:55
  • Just to remove all doubt, please quote the line of your code where you plan to show the data, oh and PREceded by the line showing the source of the data – g00se Jul 26 '21 at 14:03
  • "peripheralTextView.append("Device Name: " + result.getDevice().getName() + " rssi: " + result.getRssi() +" Packet length: " + PacketLength + " Packet Data: " + "0x" + R + "\n");" Currently prints data onto the App including the hex stream. i want the hex stream represented as each digit separately in an array. The data is coming from a CC2640 BLE Development board and I'm using the scan call back function to scan from the broadcasted advertisement packets, which include this hex stream – GAZZA Jul 26 '21 at 14:06
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/235305/discussion-between-g00se-and-joshua-gascoigne). – g00se Jul 26 '21 at 14:33