Android BLE通讯详解,连接JDY-19

栏目: Android · 发布时间: 6年前

内容简介:最近做一款Android与蓝牙BLE设备通讯的项目,记录下开发经验。蓝牙设备是JDY-19模块,串口透传,非常方便好用。官方教程需要创建Service进行通讯,此处需求为简单数据透传,直接在Activity中收发完成就结束,不开启服务,简单便捷。话不多说,代码伺候。一次连接需要的数据是扫描后的device对象,连接哪个设备就使用这个device对象进行后续连接

最近做一款Android与蓝牙BLE设备通讯的项目,记录下开发经验。

蓝牙设备是JDY-19模块,串口透传,非常方便好用。官方教程需要创建Service进行通讯,此处需求为简单数据透传,直接在Activity中收发完成就结束,不开启服务,简单便捷。话不多说,代码伺候。

一、Android扫描BLE设备

0. 开启权限

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

1. 检查是否有BLE支持

    if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
        Toast.makeText(this, "您的设备不支持蓝牙BLE", Toast.LENGTH_SHORT).show();
        finish();
    }
 

2.检查是否有蓝牙支持

    final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
    mBluetoothAdapter = bluetoothManager.getAdapter();
    if (mBluetoothAdapter == null) {
        Toast.makeText(this, "您的设备不支持蓝牙", Toast.LENGTH_SHORT).show();
        finish();
    }
 

3.如果本地蓝牙没有开启,请求打开

    if (!mBluetoothAdapter.isEnabled()) {
        //两种方式
        //1.请求用户打开
        // 我们通过startActivityForResult()方法发起的Intent将会在onActivityResult()回调方法中获取用户的选择,比如用户单击了Yes开启,
        // 那么将会收到RESULT_OK的结果,
        // 如果RESULT_CANCELED则代表用户不愿意开启蓝牙
        Intent mIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(mIntent, REQUEST_ENABLE_BT);
 
        // 2.用enable()方法来开启,无需询问用户(无声息的开启蓝牙设备),这时就需要用到android.permission.BLUETOOTH_ADMIN权限。
        //mBluetoothAdapter.enable();
        // mBluetoothAdapter.disable();//关闭蓝牙
    }
 

4.开始扫描LE设备,这个API要求 targetSdkVersion 必须低于21 , minSdkVersion必须大于等于18

scanLeDevice(true);
 
 
private void scanLeDevice(final boolean enable) {
    if (enable) {
        // Stops scanning after a pre-defined scan period.
 
        //是否需要自动停止扫描
//            mHandler.postDelayed(new Runnable() {
//                @Override
//                public void run() {
//                    mScanning = false;
//                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
//                }
//            }, SCAN_PERIOD); //
 
        mScanning = true;
        mBluetoothAdapter.startLeScan(mLeScanCallback);
    } else {
        mScanning = false;
        mBluetoothAdapter.stopLeScan(mLeScanCallback);
    }
}
 

5.扫描结果

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() {
                for (Map<String, Object> item : mData) {
                    if (item.get("mac").equals(device.getAddress())) {
                        item.put("rssi",rssi);
                        adapter.notifyDataSetChanged();
                        return;
                    }
                }
                Log.i("CTLockBLE", "device found name:" + device.getName() + " mac:" + device.getAddress() + " type:" + device.getType() + " rssi:" + rssi);
                if (device.getAddress() == null) {
                    return;
                }
                if (scanRecord.length != 62 || scanRecord[20 - 6] != (byte) 0xa0) {
                    //不是透传模块
                    return;
                }
                Map<String, Object> itemData = new HashMap<>();
                itemData.put("dev",device);
                itemData.put("name", device.getName());
                itemData.put("mac", device.getAddress());
                itemData.put("type", device.getType());
                itemData.put("rssi", rssi);
                itemData.put("sr", scanRecord);
                mData.add(itemData);
                adapter.notifyDataSetChanged();
            }
        });
    }
};
 
 

6.保存扫描结果

一次连接需要的数据是扫描后的device对象,连接哪个设备就使用这个device对象进行后续连接

二、与蓝牙BLE通讯

package cn.nodemedia.ctlockble;
 
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.util.Log;
 
public class BLEControl {
 
    public interface BLEControlCallback {
        void onConnStatus(int status);
    }
 
    private final static String TAG = "CTBLE.BLEControl";
    private static final int STATE_DISCONNECTED = 0;
    private static final int STATE_CONNECTING = 1;
    private static final int STATE_CONNECTED = 2;
 
    public static String Service_uuid = "0000ffe0-0000-1000-8000-00805f9b34fb";
    public static String Characteristic_uuid_TX = "0000ffe1-0000-1000-8000-00805f9b34fb";
    public static String Characteristic_uuid_RX = "00002902-0000-1000-8000-00805f9b34fb";
    public static String Characteristic_uuid_FUNCTION = "0000ffe2-0000-1000-8000-00805f9b34fb";
 
    private Context mContext;
    private BluetoothDevice mDevice;
    private BluetoothGatt mBluetoothGatt;
    private BLEControlCallback mCallback;
 
    private int mConnectionState = STATE_DISCONNECTED;
    private boolean isSubscribe = false;
 
    public BLEControl(Context context, BluetoothDevice device, BLEControlCallback callback) {
        this.mContext = context;
        this.mDevice = device;
        this.mCallback = callback;
    }
 
    public int connect() {
        mConnectionState = STATE_CONNECTING;
        this.mBluetoothGatt = mDevice.connectGatt(mContext, true, mGattCallback);
        return this.mBluetoothGatt == null ? -1 : 0;
    }
 
    public void disconnect() {
        if (mBluetoothGatt != null) {
            mCallback.onConnStatus(1004);
            mBluetoothGatt.close();
        }
    }
 
    void deley(int ms) {
        try {
            Thread.currentThread();
            Thread.sleep(ms);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
 
    public int updateRom() {
 
        return 0;
    }
 
 
    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                mConnectionState = STATE_CONNECTED;
                Log.i(TAG, "Connected to GATT server.");
                Log.i(TAG, "Attempting to start service discovery:" + mBluetoothGatt.discoverServices());
//                runOnUiThread(() -> );
                mCallback.onConnStatus(1000);
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                mConnectionState = STATE_DISCONNECTED;
                Log.i(TAG, "Disconnected from GATT server.");
//                runOnUiThread(() -> stateTv.setText("设备已断开"));
                mCallback.onConnStatus(1002);
            }
        }
 
        @Override
        // New services discovered
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                Log.d(TAG, "onServicesDiscovered received: " + status);
 
                for (BluetoothGattService service : mBluetoothGatt.getServices()) {
                    Log.d(TAG, "get service uuid " + service.getUuid() + " type:" + service.getType());
                    for (BluetoothGattCharacteristic gc : service.getCharacteristics()) {
                        Log.d(TAG, "get service gc uuid " + gc.getUuid());
                        for (BluetoothGattDescriptor descriptor : gc.getDescriptors()) {
                            Log.d(TAG, "get service gc descriptor uuid " + descriptor.getUuid());
                            isSubscribe = mBluetoothGatt.setCharacteristicNotification(gc, true);
                            if (isSubscribe) {
                                descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                                mBluetoothGatt.writeDescriptor(descriptor);
                                mCallback.onConnStatus(1001);
                            } else {
                                Log.e(TAG, "setCharacteristicNotification error");
                                mCallback.onConnStatus(1003);
                            }
 
                        }
                    }
                }
 
 
            } else {
                Log.w(TAG, "onServicesDiscovered received: " + status);
            }
        }
 
        @Override
        // Result of a characteristic read operation
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic,
                                         int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                Log.d(TAG, "onCharacteristicRead received: " + status);
            }
        }
 
        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            Log.d(TAG, "onCharacteristicChanged " + characteristic.getValue().length);
        }
    };
}

0.创建对象

传入Context对象,扫描后得到的device对象,当前Activity类implements BLEControl.BLEControlCallback后的 this指针

 bleControl = new BLEControl(this, mDevice, this);
 

1.实现事件监听器

@Override
public void onConnStatus(int status) {
    Log.d("CTBLE.CommActivity", "OnConnStatus " + status);
    runOnUiThread(() -> {
        switch (status) {
            case 1000:
                stateTv.setText("设备已连接");
                break;
            case 1001:
                stateTv.setText("设备已订阅");
                break;
            case 1002:
                stateTv.setText("设备已断开");
                break;
            case 1003:
                stateTv.setText("设备订阅失败");
                break;
            case 1004:
                stateTv.setText("设备已注销");
                break;
        }
    });
}
 

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Realm of Racket

Realm of Racket

Matthias Felleisen、Conrad Barski M.D.、David Van Horn、Eight Students Northeastern University of / No Starch Press / 2013-6-25 / USD 39.95

Racket is the noble descendant of Lisp, a programming language renowned for its elegance and power. But while Racket retains the functional goodness of Lisp that makes programming purists drool, it wa......一起来看看 《Realm of Racket》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具