Android(Java)开发之获取BLE广播包(扫描后获取:广播数据+扫描应答数据+RSSI)

来源:互联网 发布:中国石油大学网络教育 编辑:程序博客网 时间:2024/06/08 08:41

一、安卓BLE的广播包数据从哪获取?

通常,安卓APP读写BLE设备的数据都是建立连接后通过GATT获取或修改。但是,BLE设备向外广播时本身会携带一部分有用信息,如将传感数据存放到广播包的自定义数据段,最近接触的一个iBeacon/EddyStone整合项目便是类似,因此为了提取广播包进行解析,首要问题就是安卓APP如何获取广播数据。

      其实,安卓蓝牙在扫描设备后,回调方法 onLeScan(...)中的参数 scanRecord 就是广播数据,这里同时包含广播数据扫描应答数据(均为31字节),所以长度一般就是 62 字节,BLE4.0规定,如果广播包和扫描应答包不足字节,则以0补齐。如下:

 private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {        @Override        public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {                 ...//scanRecord存放广播和扫描应答数据,rssi存放扫描获取的rssi值        }    };


二、如何存储广播包数据并解析?

1.目标:扫描的设备显示List中除了显示设备名、Mac地址外,还加入完整的scanRecord数据包显示和rssi的显示。

2.步骤:(基于官方BLE Demo)

(1)在ListView的适配器创建中加入RSSI和Record列表

 //ListView Adapter,用于在listview里管理扫描到的设备    private class LeDeviceListAdapter extends BaseAdapter {        private ArrayList<BluetoothDevice> mLeDevices;        private ArrayList<Integer> mRSSIs;//新加        private ArrayList<byte[]> mRecords;//新加        private LayoutInflater mInflator;        public LeDeviceListAdapter() {            super();            mLeDevices = new ArrayList<BluetoothDevice>();            mRSSIs = new ArrayList<Integer>();//新加            mRecords = new ArrayList<byte[]>();//新加            mInflator = DeviceScanActivity.this.getLayoutInflater();        }        ...}

(2)改写LeDeviceListAdapter适配器中的addDevice方法

        public void addDevice(BluetoothDevice device,int rssi,byte[] scanRecord) {            if(!mLeDevices.contains(device)) {                mLeDevices.add(device);                mRSSIs.add(rssi);//新加                mRecords.add(scanRecord);//新加            }        }


(3)BluetoothAdapter.LeScanCallback回调方法onLeScan()中调用新的addDevice方法

    // Device scan callback.    private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {        @Override        public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {            runOnUiThread(new Runnable() {                @Override                public void run() {                    mLeDeviceListAdapter.addDevice(device,rssi,scanRecord);                    mLeDeviceListAdapter.notifyDataSetChanged();                }            });        }    }


(4)ViewHolder和XML布局中加入对应的TextView,并改写LeDeviceListAdapter适配器中的getView()方法:用于支持scanRecord和rssi的列表化显示

    static class ViewHolder {        TextView deviceName;        TextView deviceAddress;        TextView deviceBroadcastPack;//加入广播包数据        TextView deviceRssi;//加入RSSI    }

       @Override        public View getView(int i, View view, ViewGroup viewGroup) {            ViewHolder viewHolder;            // General ListView optimization code.            if (view == null) {                view = mInflator.inflate(R.layout.listitem_device, null);                viewHolder = new ViewHolder();                viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address);                viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name);                viewHolder.deviceBroadcastPack = (TextView) view.findViewById(R.id.device_broadcastPack);                viewHolder.deviceRssi = (TextView) view.findViewById(R.id.device_rssi);                                view.setTag(viewHolder);            } else {                viewHolder = (ViewHolder) view.getTag();            }            //获取设备列表、RSSI列表和广播包列表项目            BluetoothDevice device = mLeDevices.get(i);            int rssi = mRSSIs.get(i);            byte[] scanRecord = mRecords.get(i);              //提取信息            final String deviceName = "设备名:" + device.getName();            final String deviceAddr = "Mac地址:" + device.getAddress();            final String broadcastPack ="广播包:" + bytesToHex(scanRecord);//此处调用了格式转换方法bytesToHex()将十六进制序列转String            final String rssiString = "RSSI:" + String.valueOf(rssi) + "dB";//此处调用了String的格式转换方法valueOf()将数值类型转String                     //显示数据            if (deviceName != null && deviceName.length() > 0)//显示设备名                viewHolder.deviceName.setText(deviceName);            else                viewHolder.deviceName.setText(R.string.unknown_device);            viewHolder.deviceAddress.setText(deviceAddr);//显示设备地址            viewHolder.deviceBroadcastPack.setText(broadcastPack);//显示广播包            viewHolder.deviceRssi.setText(rssiString);//显示RSSI                       return view;        }

(5)实现scanRecord的格式转换方法:bytesToHex()---广播和扫描应答数据以十六进制格式存储在bytes[]数组中,需要转换为String后显示

    //scanRecords的格式转换    static final char[] hexArray = "0123456789ABCDEF".toCharArray();    private static String bytesToHex(byte[] bytes) {        char[] hexChars = new char[bytes.length * 2];        for (int j = 0; j < bytes.length; j++) {            int v = bytes[j] & 0xFF;            hexChars[j * 2] = hexArray[v >>> 4];            hexChars[j * 2 + 1] = hexArray[v & 0x0F];        }        return new String(hexChars);    }

三、效果展示

实现的基本效果如图,扫描到的设备列表加入了RSSI和完整广播包的显示


后注:获取到scanRecord后就能根据协议解析广播包了,根据需求提取对应字段。scanRecord的数据必须进行格式转换,否则列表显示会出现乱码,格式不匹配!



2 0
原创粉丝点击