Gửi dữ liệu đến BLESerial3 bằng Bluetooth LE Android

5/5 - (5 votes)

Về cơ bản kết nối đến các thiết bị đều giống nhau và tuỳ vào thiết bị mà có thêm vào bước xử lý đặc biệt khác.

Có 2 loại Bluetooth: Bluetooth Classic và BLE (Android 4.3 trở lên mới được hỗ trợ BLE)

Muốn kết nối tới một thiết bị dùng Bluetooth LE thì ta cần một đối tượng của lớp BluetoothDevice. Để có đối tượng này được trả về khi bạn scan thiết bị xung quanh, việc còn lại của bạn là lọc, so sánh xem đã đúng module cần kết nối hay chưa.

Thông thường chúng ta lọc bằng tên của thiết bị hoặc địa chỉ MAC của thiết bị. (bên iOS không có quyền lấy địa chỉ mac)

Sau khi tìm thấy module cần kết nối thì chúng ta cần xem thiết bị đó có những Service gì và có Service mình cần thì dùng hay không thông qua SERVICE_UUID. Cuối cùng ghi hay đọc lên các đặc tính của Service đó.

Tóm lại các bước ghi đó là:

  • Quét các thiết bị trong phạm vi và lọc device bằng địa chỉ MAC hoặc tên.
  • Kết nối bằng đối tượng của lớp BluetoothDevice.
  • Khám phá các dịch vụ và khám phá các đặc tính có trong những dịch vụ đó.
  • Ghi giá trị lên đặc tính.
  • Đọc giá trị từ đặc tính.

Thống nhất khái niệm

  • Device/Client/Module tức là cái BLESerial3.
  • Điện thoaị là thiết bị chạy Android trên 4.3.
  • Ble là Bluetooth Low energy.
  • BC là Bluetooth classic.

Định nghĩa các UUID

Chú ý UUID chữ hoa và thường có khác nhau. Nêú để chữ in hoa bạn sẽ không thể kết nối đến được device. Dưới đây là thông tin SERVICE dùng để kết nối và CHARACTERISTIC_UUID để ghi. (Module này không cho phép đọc nhé!)

private static UUID SERVICE_UUID = UUID.fromString("feed0001-c497-4476-a7ed-727de7648ab1");
private static final UUID CHARACTERISTIC_UUID = UUID.fromString("feedaa02-c497-4476-a7ed-727de7648ab1");

Những UUID này lấy ở đâu? Khi nhà sản xuất họ làm ra phần cứng thì họ sẽ nạp cố định cho nó nên bạn là nhà phát triển bạn có thể yêu cầu họ cung cấp thông tin này cho mình.

Hình ảnh BLESerial3

ảnh BLESerial3 module (credit: Tomonari-t)

Cài đặt mã nguồn

Quét các thiết bị trong phạm vi và lọc device.

Ở đây cần tìm ra một thiết bị mình cần với điều kiện trùng SERVICE UUID. Sau khi tìm ra thì cần trả kết quả về thông qua đối tượng callback là mScanCallback. 

ScanSettings settings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).setReportDelay(1000).build();
ScanFilter scanFilter = new ScanFilter.Builder().setServiceUuid(new ParcelUuid(SERVICE_UUID)).build();
mScanner.startScan(Arrays.asList(scanFilter), settings, mScanCallback);

Thằng mScanCallback này sẽ được khởi tạo ở bước tiếp theo

Kết nối bằng địa chỉ MAC

private final ScanCallback mScanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            Log.i(TAG, "onScanResult: " + result.getDevice().getAddress());
        }

        @Override
        public void onBatchScanResults(List<ScanResult> results) {
            if (!results.isEmpty()) {
                ScanResult result = results.get(0);
                BluetoothDevice device = result.getDevice();
                String deviceAddress = device.getAddress();
                BluetoothDevice bleDevice = mBluetoothAdapter.getRemoteDevice(deviceAddress);
                mBluetoothGatt =bleDevice.connectGatt(MainActivity.this, false, mGattCallback);
            }
        }

        @Override
        public void onScanFailed(int errorCode) {
            stopLeScan();
            Log.i(TAG, "onScanFailed");

        }
    };

Ở đây ta tìm thấy được thiết bị ở bước trên thì lấy địa chỉ mac của nó, sau đó thực hiện kết nối tới GATT server trên device bằng hàm connectGatt. Sau khi gọi connectGatt thì kết quả dù được hay không nó đều tiếp tục thông báo cho mình biết thông qua một callback là mGattCallBack. Thằng mGattCallBack được khởi tạo ở bước tiếp theo.

Khám phá các dịch vụ và khám phá các đặc tính

Ở bước này ta cần khởi tạo callback để lấy kết quả từ việc kết nối tới device.

private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            Log.i(TAG, "Changed-" + newState);
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                gatt.discoverServices();
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                Log.i(TAG, "Mat ket noi");
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                BluetoothGattService service = gatt.getService(SERVICE_UUID);
                if (service != null) {
                    // do something
                }
            } else {
                Log.w(TAG, "onServicesDiscovered received: " + status);
            }
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);
            if (status != BluetoothGatt.GATT_SUCCESS) {
                Log.d("onCharacteristicWrite", "Failed write, retrying");
            } else {
                Log.d("onCharacteristicWrite", "succ 11111");
            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                Log.d(TAG, "Succesfully read characteristic: " + characteristic.getValue().toString());
            } else {
                Log.d(TAG, "Characteristic read not successful");
            }

        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            Log.i(TAG, "onCharacteristicChanged");
        }
    };

Trong đối tượng callback này ta kiểm tra nếu kết nối thành công thì sẽ khám phá service của nó thông qua hàm discoverServices(); còn không sẽ báo ra không kết nối được. Nếu khám phá ra service thì nó sẽ tự động gọi hàm onServicesDiscovered.

Ngoài ra trong đối tượng này ta còn lắng nghe các sự kiện khác như:

onCharacteristicWrite: Hàm này sẽ kích hoạt khi ghi xong một giá trị vào device (được chạy ngay sau khi ta gọi hàm writeCharacteristic)
onCharacteristicRead: Hàm này sẽ kích hoạt khi đọc xong một giá trị từ device (được chạy ngay sau khi ta gọi hàm readCharacteristic).
onCharacteristicChanged: Hàm này sẽ kích hoạt khi một đặc tính bị thay đổi trên device (ví dụ như nhịp tim thay đổi trên device thì trên app của bạn cũng tự động lấy được giá trị đó).

Ghi giá trị lên đặc tính

Coi như mọi thứ đã thành công và đây là bước ghi một giá trị lên device của bạn.

Bởi vì theo quy định BLE chỉ được gửi tối đa 20 bytes một lần, vậy nên nếu bạn phải gửi gói nào nặng hơn 20 bytes thì cần phải chia ra làm nhiều gói mỗi gói nhỏ hơn hoặc bằng 20 bytes. Mỗi gói gửi cách nhau tầm 150 mili giây. Mình sẽ chia sẻ đoạn code đấy cho bạn luôn  lovedog

public void writeCustomCharacteristic() {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        BluetoothGattService mCustomService = mBluetoothGatt.getService(SERVICE_UUID);
        if (mCustomService == null) {
            Log.w(TAG, "Custom BLE Service not found");
            return;
        }
        BluetoothGattCharacteristic mWriteCharacteristic = mCustomService.getCharacteristic(CHARACTERISTIC_UUID);
        if (mWriteCharacteristic != null) {
            isWritted = false;
            int i;
            for (i = 0; i < splitStringArr.size(); i++) {
                mWriteCharacteristic.setValue(splitStringArr.get(i));
                boolean codeCheck = mBluetoothGatt.writeCharacteristic(mWriteCharacteristic);
                if (!codeCheck) {
                    Log.w(TAG, "ghi fail lan" + i);
                } else {
                    Log.w(TAG, "ghi thanh cong lan" + i);
                }
                try {
                    Thread.sleep(150);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            splitStringArr.clear();
            isWritted = true;
        } else {
            Log.w(TAG, "characteristic bi null");
        }
    }

Đọc giá trị từ đặc tính

Do module này nó không cho phép đọc nên mình sẽ không làm thực tế, nhưng để dùng cho các dự án khác thì mình nghĩ đoạn code này bạn có thể tham khảo.

public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            LogUtils.logDebug(TAG + "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.readCharacteristic(characteristic);
    }

Dừng việc quét thiết bị

Sau khi tìm thấy thì dừng việc quét lại nhé không thì hao mòn pin điện thoại đấy.

private void stopLeScan() {
        if (mScanning) {
            mScanning = false;
            mScanner.stopScan(mScanCallback);
        }
    }

Trên đây là tổng quan việc kết nối và gửi dữ liệu thông qua bluetooth năng lượng thấp, mong rằng với tài liệu ít ỏi này sẽ giúp ích được cho công việc của bạn. Nếu bạn muốn biết sâu hơn thì có lẽ nên nghiên cứu thêm trên mạng hoặc mua sách. Bạn có bất kỳ đóng góp nào hãy bình luận để chúng ta cùng trao đổi.

Xin cám ơn.

Các vấn đề BLE trên Android cần biết:

  1. Không scan nếu chưa bật GPS (Rule của Android)
  2. Không scan kể cả đã bật GPS (vd: Google Pixel, Samsung S9… lúc này chỉ cần khởi động lại máy là bình thường)
  3. Thỉnh thoảng vào GATT error mà không rõ nguyên nhân khi lắng nghe Broadcast (mình không rõ nguyên nhân do đâu)
  4. Thiết bị Huawei chạy không ổn định, có lúc quét một lúc rồi tự động dừng (mình không rõ nguyên nhân do đâu)

Tất cả các bài viết mình đã đọc trong khi làm việc với ble:

  1. Summary 1 http://arduino.vn/bai-viet/1181-vbluno-tutorial-2-tong-quan-ve-cong-nghe-ble-va-vi-du-minh-hoa-phan-1
  2. Summary 2 http://arduino.vn/bai-viet/1189-vbluno-tutorial-2-tong-quan-ve-cong-nghe-ble-va-vi-du-minh-hoa-phan-2
  3. Quyển sách rất hay nhưng mất phí http://www.althosbooks.com/intobleb.html
  4. Các giao thức và thuộc tính https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3478807/
  5. Sóng vô tuyến là gì? http://arduino.vn/bai-viet/277-song-vo-tuyen-la-gi-va-nhung-suc-manh-cua-no-khi-ket-hop-voi-arduino
  6. Danh sách video bluetooth traning: https://www.bluetooth.com/develop-with-bluetooth/developer-resources-tools/developer-training-videos
  7. Đọc thêm vai trò GAP http://microchipdeveloper.com/wireless:ble-gap-roles
  8. Ứng dụng của BT Classic va BLE https://www.lsr.com/static/images/bluetooth-module/bluetooth-smart-comparison.jpg
  9. Phân biệt các profile high level https://www.philips.co.in/c-f/XC000008687
  10. Bài viết chi tiết về BT https://blog.reinforce-lab.com/2013/08/13/blebook-ch2-ble-spec/#
  11. Bluetooth Stack của TOSHIBA https://www.tjsys.co.jp/embedded/netnucleus-bt/index_j.htm
  12. RFCOMM structure http://www.althos.com/tutorial/Bluetooth-tutorial-RF-serial-communication-layer.html
  13. Chi tiết về BLE https://www.slideshare.net/yeokm1/introduction-to-bluetooth-low-energy
  14. Chi tiết về BLE 2 https://www.slideshare.net/zdennis/btle-bluetooth-low-energy-and-corebluetooth
  15. Giải thích hay: http://ticketmastermobilestudio.com/blog/android-bluetooth-low-energy-tutorial
  16. Ví dụ hay về cả server và client: http://nilhcem.com/android-things/bluetooth-low-energy
  17. https://www.polidea.com/blog/Emberlight_turns_light_bulbs_into_smart_ones/
  18. http://blog.davidvassallo.me/2015/09/02/ble-health-devices-first-steps-with-android/
  19. http://mslgt.hatenablog.com/entry/2015/05/17/212257
  20. http://android-er.blogspot.jp/2016/07/bluetooth-le-example-connect-to.html
  21. Scan với điều kiện: https://blog.creatorslab.jp/2017/05/13/android-ble-service-uuid/
  22. Tham khảo thêm ví dụ: http://mslgt.hatenablog.com/entry/2015/05/17/212257

Các bài viết không xem thì tiếc:

Thảo luận

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Xem thêm
Bluetooth profiles là một tập hợp các quy tắc và…
 
 
 
 
Facetime iPhone

Main Menu