1. The difference between traditional Bluetooth and low-power Bluetooth

Simple classification:

Bluetooth can be divided into classic Bluetooth module (V1.1/1.2/2.0/2.1/3.0), low power Bluetooth module (V4.0/4.1/4.2), and Bluetooth dual-mode module (support all versions of Bluetooth, compatible with low power Bluetooth and classic Bluetooth). Bluetooth 4.0 is a comprehensive protocol specification. In addition to the new LE specification, it also includes BR/EDR specification, and in practical use, it is divided into Single mode (Single mode) and Dual mode (Dual mode) version. The former only supports LE specification and cannot communicate with the previous version of Bluetooth 4.0. The latter supports both LE and BR/EDR specifications and is compatible with older Versions of Bluetooth.

Use difference:

1. Sending and receiving tasks of low-power Bluetooth will be completed as quickly as possible. After completion, Bluetooth BLE will suspend transmitting wireless (but will still accept) and wait for the next connection to be activated; Traditional Bluetooth is about constant connectivity.

2. There are only 3 low-power Bluetooth broadcast channels (divided to ensure that the network does not interfere with each other); Traditional Bluetooth is 32.

3, low-power Bluetooth “complete” a connection (that is, scan other devices, establish links, send data, authenticate and properly end) only 3ms; Traditional Bluetooth takes hundreds of milliseconds to complete the same connection cycle.

4. Low power Bluetooth uses very short data packet, which is mainly used for products with high real-time requirements but low data rate. Remote control products such as keyboard, remote control mouse, data transmission of sensing equipment, such as heartbeat band, sphygonometer, temperature sensor, etc. Traditional Bluetooth uses a long packet length, which can be used for large data transmission, such as voice, music, high data transmission, etc.

5. Low-power Bluetooth has no power level. Generally, the transmission power is +4dBm and the transmission distance is up to 70m in the open distance. Traditional Bluetooth has three power levels, Class1, Class2, and Class3, supporting 100m, 10m, and 1M transmission distances respectively.

Protocol stack differences:

Profiles defines a real-world application scenario. Specifically, Profiles defines the functions and features required for each layer of a Bluetooth system, from the PHY (physical layer) to L2CAP, as well as for any other protocol outside the core specification. Traditional Bluetooth uses Profiles SPP and REFCOMM, while LE Bluetooth uses Profiles GATT/ATT, which are incompatible with each other.

To understand the GAP

GAP, or Universal Access Profile, is the basic profile that all Bluetooth devices must implement. It defines the basic requirements for Bluetooth devices. For example, for BR/EDR, it defines Bluetooth devices to include radio, baseband, link manager, L2CAP, and service discovery protocol capabilities; For LE, it defines the physical layer, link layer, L2CAP, security manager, GATT/ATT. This connects all the layers together to form the basic requirements of a Bluetooth device. It also describes the behavior of device searching, connection establishment, security, authentication, association model, and service searching.

    • In BR/EDR, GAP defines each device as a single role that may have functions such as how devices discover each other, establish connections, and describe the security association model used for authentication. The device may have only one or more of these functions, such as the ability to start or accept connections only.
    • In LE, GAP defines four specific roles: Broadcaster, Observer, Peripheral and Central. If the underlying Controller supports these roles or combinations of roles, the device can also support multiple roles simultaneously. However, only one of these characters can be supported at a time. Each role specifies the requirements of the underlying Controller. This allows the controller to be optimized for a specific use case.

Whereas traditional Bluetooth protocols treat each Bluetooth device as an equal role, LE Bluetooth allows us to assign different roles to different devices based on their actual use, further optimizing transmission speed and power consumption according to the role characteristics.

Traditional Bluetooth protocol stack

Logical Link Control and Adaptation Protocol (L2CAP)

The L2CAP protocol is at the Adaption Layer. As the name implies, it provides interfaces for high-level protocols such as SDP and REFCOMM and communicates with low-level HCI. HCI protocol provides a unified interface to access bluetooth chip and is burned into bluetooth chip. Android development does not need to consider protocol layers below the Adaption Layer.

L2CAP transmission is based on the concept of a channel, similar to the fifO communication channel, it is a point-to-point channel, each channel has an independent channel identifier (CID). The CID identifies each end of a channel. Some fixed CID are used for special purposes, such as a signaling channel. The fixed CID=0x0001 (LE is fixed 0x0005). This channel is used to create and establish connection-oriented data channels and to negotiate changes in the characteristics of these channels. After the two ends negotiate the configuration information, another channel is dynamically allocated. After 0x0040, these channels are dynamically allocated. These channels are used for upper-layer data transmission. The following figure illustrates how L2CAP inter-entity communication is used between different devices:

The logical channel can work in five different modes (which can be understood as five different usage scenarios), the last of which is unique to the LE device:

  1. Basic L2CAP Mode(Equivalent to L2CAP Specification in Bluetooth V1.1) Default Mode. If no other Mode is selected, use this Mode.
  2. Flow Control Mode In this Mode, no retransmission is performed, but lost data can be detected and reported as lost.
  3. Retransmission Mode This Mode ensures that all data packets can be successfully transmitted to the peer device.
  4. Enhanced Retransmission Mode The Enhanced Retransmission Mode is similar to the Retransmission Mode in that it incorporates poll-bit (a polling mechanism) to improve recovery efficiency.
  5. Streaming Mode In this Mode, the packet is numbered but does not require ACK confirmation for real time transmission. Set a timeout timer to flush out the timeout data once the timer expires.
  6. LE Credit Based Flow Control Mode Is used for LE device communication.

REFCOMM agreement

At the transport layer, RFCOMM provides serial (9-pin RS-232) emulation based on the L2CAP protocol, supporting up to 60 communication connections between two Bluetooth devices. The purpose of RFCOMM is to ensure a complete communication path between applications on two different devices (both ends of the communication) and to maintain a communication segment between them.

SPP protocol

A Serial Port Profile defines protocols and procedures for simulating RS232 (or similar) Serial ports using Bluetooth. This is also the main function of Bluetooth from the beginning: to replace RS232 wired communication, to connect multiple devices wirelessly, to overcome the synchronization problem.

Attribute Protocol (ATT)

Simply put, the ATT layer is used to define user commands and the data that the commands operate on, such as reading or writing data. In the BLE protocol stack, developers are most exposed to ATT. BLE introduces the concept of attribute to describe a piece of data. Attribute defines not only data, but also ATT commands that can be used on the data. Therefore, this layer is called the ATT layer.

Generic Attribute Profile (GATT)

GATT is used to regulate the data content in attributes and use the concept of group to manage attributes. Without GATT, BLE protocol stack can run, but connectivity will be problematic. It is because of GATT and various application profiles that BLE gets rid of the compatibility dilemma of wireless protocols such as ZigBee and becomes the 2.4g wireless communication product with the largest shipments.

Service Discovery Protocol (SDP)

The SDP protocol lets client applications discover the services provided by existing server applications and the properties of those services. SDP provides only a mechanism for discovering services, not a means of using them. Each Bluetooth device requires an SDP Service, except for bluetooth devices that only serve as clients. A collection of all information found in a Servcie is a service Record:

Each service record consists of multiple Service attributes, which are composed of attribute ids and attribute values. The Attribute ID is defined by Assigned Value. For example, the Record Handle Attribute ID is 0x0000.

The UUID is a 128 – bit globally unique identifier, preset UUID can query in the bluetooth’s official website: www.bluetooth.com/specificati… .

* The benefit of using a preset UUID is that the client can quickly and accurately identify what the server is and what services it can provide. Many mobile systems have added services and code that use preset Uids so that information uploaded from the server (from devices such as blood pressure monitors, heart rate monitors, thermometers, etc.) can be correctly interpreted without having to install another App. *

Traditional Bluetooth connection process

One device initiates a request to connect to another device.

Another device waits for another device to initiate a connection request.

Traditional Bluetooth development for Android

To prepare

1. Declare bluetooth permissions
``` <manifest ... > <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <! -- If your app targets Android 9 or lower, you can declare ACCESS_COARSE_LOCATION instead. --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> ... </manifest>Copy the code
2. To obtainBluetoothAdapter
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null) {
    // Device doesn't support Bluetooth
}
Copy the code
3. Enable Bluetooth
``` if (! bluetoothAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); }Copy the code

Call isEnabled() to check whether Bluetooth is currently enabled. If this method returns false, bluetooth is disabled. Calls startActivityForResult (enableBtIntent REQUEST_ENABLE_BT); A dialog box will be displayed asking the user for permission to enable Bluetooth.

Search equipment

New device ————> Paired Device ————> Connect Device

A, scanning equipment is a major operation Second, never matched equipment, if not enabled "detectable", cannot be scanned to the device information Three, pairing * * * * refers to the existence of two devices knowing each other, can be used for authentication of Shared link key, and the ability to establish encrypted connection with each other. 4. ** connection ** means that devices currently share an RFCOMM channel and are able to transmit data to each other. The current Android Bluetooth API requirements state that an RFCOMM connection can only be established after the device has been paired. Pairing is performed automatically when an encrypted connection is initiated using the Bluetooth API.Copy the code
1. Query paired devices

Before performing device discovery, you can query the paired device set to see if the desired device is in the detected state.

Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();

if (pairedDevices.size() > 0) {
    // There are paired devices. Get the name and address of each paired device.
    for (BluetoothDevice device : pairedDevices) {
        String deviceName = device.getName();
        String deviceHardwareAddress = device.getAddress(); // MAC address
    }
}
Copy the code
2. Discover devices

To start discovering devices, simply call startDiscovery(). The process is an asynchronous operation and returns a Boolean indicating whether the discovery process started successfully. The discovery process typically involves a query scan of about 12 seconds, followed by a page scan of each discovered device to retrieve its Bluetooth name. A BroadcastReceiver should be registered for ACTION_FOUND Intents to receive information about each found device. The system broadcasts the Intent for each device. The Intent contains the extra fields EXTRA_DEVICE and EXTRA_CLASS, BluetoothDevice, which contains key information such as device name and MAC address, and BluetoothClass, which contains device types such as computer, mobile phone, headset and usage information such as audio and telephone, respectively.

@Override protected void onCreate(Bundle savedInstanceState) { ... // Register for broadcasts when a device is discovered. IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); registerReceiver(receiver, filter); } // Create a BroadcastReceiver for ACTION_FOUND. private final BroadcastReceiver receiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (BluetoothDevice.ACTION_FOUND.equals(action)) { // Discovery has found a device. Get the BluetoothDevice // object and its info from the Intent. BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); String deviceName = device.getName(); String deviceHardwareAddress = device.getAddress(); // MAC address } } }; @Override protected void onDestroy() { super.onDestroy(); . // Don't forget to unregister the ACTION_FOUND receiver. unregisterReceiver(receiver); }Copy the code
3. Enable detectability (device visibility)

By default, the device stays in detectable mode for 120 seconds (2 minutes). Define different durations up to 3600 seconds by adding the EXTRA_DISCOVERABLE_DURATION Extra property. ,

Intent discoverableIntent =
        new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
Copy the code

During this process, the phone will pop up and ask the user to turn on the visibility. The Activity will receive a call to the onActivityResult() callback with a result code equal to the duration that the device can detect. If the user selects no or an error occurs, the result code is RESULT_CANCELED. Listen for announcements of visibility changes: Register BroadcastReceiver for ACTION_SCAN_MODE_CHANGED Intent. The Intent will contain the extra fields EXTRA_SCAN_MODE and EXTRA_PREVIOUS_SCAN_MODE, which provide the new and old scan modes, respectively. Each Extra attribute may have the following values:

  • SCAN_MODE_CONNECTABLE_DISCOVERABLEThe device is in detectable mode.
  • SCAN_MODE_CONNECTABLEDevice not in detectable mode, but still receiving connection.
  • SCAN_MODE_NONEThe device is not in detectable mode and cannot receive a connection.

Connected devices

Traditional Bluetooth communication is done by setting up REFCCOM Sockect. Similar to socket communication, one device needs to open the server socket and be in the LISTEN state, while the other device uses the MAC address of the server to initiate the connection. Once the connection was established, both the server and the client communicated by reading and writing to the BluetoothSocket.

1. The server listens for connections

When you need to connect two devices, one of them must keep an open BluetoothServerSocket to act as a server. After accepting the connection, create the BluetoothSocket and then recycle the BluetoothServerSocket unless there are more devices to connect to.

  1. By calling thelistenUsingRfcommWithServiceRecord()To obtainBluetoothServerSocket.
  2. By calling theaccept()Start listening for connection requests.
  3. If you do not need to accept more connections, callclose()
// Since 'accept()' is a blocking call, Therefore, you should not make this call in the main Activity interface Thread private Class AcceptThread extends Thread {private final BluetoothServerSocket mmServerSocket; public AcceptThread() { // Use a temporary object that is later assigned to mmServerSocket // because mmServerSocket is final. BluetoothServerSocket tmp = null; Try {//UUID is a standardized 128-bit format that can be used by string IDS to uniquely identify information. The peculiarity of a UUID is that it is large enough that you can choose any random ID without conflicting with any other ID. tmp = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID); } catch (IOException e) { Log.e(TAG, "Socket's listen() method failed", e); } mmServerSocket = tmp; } public void run() { BluetoothSocket socket = null; // Keep listening until exception Occurs or a socket is returned. While (true) {try {/** * This is a blocking call. The call is called back when the server accepts a connection or an exception occurs. The server will accept connections only if the remote device sends a connection request with a UUID that matches the UUID that * registered with this listening server socket. * Upon successful connection, 'accept()' will return the connected 'BluetoothSocket' **/ socket = mmServerSocket.Accept (); } catch (IOException e) { Log.e(TAG, "Socket's accept() method failed", e); break; } if (socket ! = null) { // A connection was accepted. Perform work associated with // the connection in a separate thread. manageMyConnectedSocket(socket); mmServerSocket.close(); break; } } } // Closes the connect socket and causes the thread to finish. public void cancel() { try { /** * This method call frees the server socket and all its resources, but does not close the connected 'BluetoothSocket' returned by 'accept()' *. Unlike TCP/IP, RFCOMM only * allows one connected client per channel at a time, so in most cases you can * use 'close()' in 'BluetoothServerSocket' immediately * after accepting a connected * socket. **/ mmServerSocket.close(); } catch (IOException e) { Log.e(TAG, "Could not close the connect socket", e); }}}Copy the code
2. The client initiates a connection
1. First get the 'BluetoothDevice' object representing the remote device 2. Invoke the BluetoothDevice ` createRfcommSocketToServiceRecord (UUID) ` get ` BluetoothSocket ` 3. Initiates the connection by calling 'connect()'. Note that this method is a blocking callCopy the code
// Since 'connect()' is a blocking call, you should always perform this connection step in a // thread outside the main Activity (interface) thread. private class ConnectThread extends Thread { private final BluetoothSocket mmSocket; private final BluetoothDevice mmDevice; public ConnectThread(BluetoothDevice device) { // Use a temporary object that is later assigned to mmSocket // because mmSocket is final. BluetoothSocket tmp = null; mmDevice = device; Try {// Get a BluetoothSocket to connect with the given BluetoothDevice. /** * This method initializes the BluetoothSocket object, So that clients can connect * to 'BluetoothDevice'. Pass UUID must be here with the server device in the * ` listenUsingRfcommWithServiceRecord (String, UUID) ` open * put its ` BluetoothServerSocket ` when used match the UUID. To use a UUID matched by *, hard-code the UUID string to your application, and then * references the string through server and client code. **/ tmp = device.createRfcommSocketToServiceRecord(MY_UUID); } catch (IOException e) { Log.e(TAG, "Socket's create() method failed", e); } mmSocket = tmp; } public void run() { // Cancel discovery because it otherwise slows down the connection. bluetoothAdapter.cancelDiscovery(); Try {// Connect to the remote device through the socket. This call blocks /** * When the client calls This method, the system will perform SDP lookup, To find the remote device with the matched *UUID. If the lookup is successful and the remote device accepts the connection, it shares the *RFCOMM channel for use during the connection, and the 'connect()' method is returned. If the connection fails, or the 'connect()' method times out (after about 12 seconds), then * this method will raise 'IOException'. **/ mmSocket.connect(); } catch (IOException connectException) { // Unable to connect; close the socket and return. try { mmSocket.close(); } catch (IOException closeException) { Log.e(TAG, "Could not close the client socket", closeException); } return; } // The connection attempt succeeded. Perform work associated with // the connection in a separate thread. manageMyConnectedSocket(mmSocket); } // Closes the client socket and causes the thread to finish. public void cancel() { try { mmSocket.close(); } catch (IOException e) { Log.e(TAG, "Could not close the client socket", e); }}}Copy the code
3. Manage REFCOMM connections
  1. usegetInputStream() 和 getOutputStream(), respectively to get the data transmission through the socket processingInputStream 和 OutputStream.
  2. useread(byte[]) 和 write(byte[])Reading data and writing it to a data stream.
public class MyBluetoothService { private static final String TAG = "MY_APP_DEBUG_TAG"; private Handler handler; // handler that gets info from Bluetooth service // Defines several constants used when transmitting messages between the // service and the UI. private interface MessageConstants { public static final int MESSAGE_READ = 0; public static final int MESSAGE_WRITE = 1; public static final int MESSAGE_TOAST = 2; / /... (Add other message types here as needed.) } private class ConnectedThread extends Thread { private final BluetoothSocket  mmSocket; private final InputStream mmInStream; private final OutputStream mmOutStream; private byte[] mmBuffer; // mmBuffer store for the stream public ConnectedThread(BluetoothSocket socket) { mmSocket = socket; InputStream tmpIn = null; OutputStream tmpOut = null; // Get the input and output streams; using temp objects because // member streams are final. try { tmpIn = socket.getInputStream(); } catch (IOException e) { Log.e(TAG, "Error occurred when creating input stream", e); } try { tmpOut = socket.getOutputStream(); } catch (IOException e) { Log.e(TAG, "Error occurred when creating output stream", e); } mmInStream = tmpIn; mmOutStream = tmpOut; } public void run() { mmBuffer = new byte[1024]; int numBytes; // bytes returned from read() // Keep listening to the InputStream until an exception occurs. while (true) { try { // Read from the InputStream. numBytes = mmInStream.read(mmBuffer); // Send the obtained bytes to the UI activity. Message readMsg = handler.obtainMessage( MessageConstants.MESSAGE_READ, numBytes, -1, mmBuffer); readMsg.sendToTarget(); } catch (IOException e) { Log.d(TAG, "Input stream was disconnected", e); break; } } } // Call this from the main activity to send data to the remote device. public void write(byte[] bytes) { try { mmOutStream.write(bytes); // Share the sent message with the UI activity. Message writtenMsg = handler.obtainMessage( MessageConstants.MESSAGE_WRITE, -1, -1, mmBuffer); writtenMsg.sendToTarget(); } catch (IOException e) { Log.e(TAG, "Error occurred when sending data", e); // Send a failure message back to the activity. Message writeErrorMsg = handler.obtainMessage(MessageConstants.MESSAGE_TOAST); Bundle bundle = new Bundle(); bundle.putString("toast", "Couldn't send data to the other device"); writeErrorMsg.setData(bundle); handler.sendMessage(writeErrorMsg); } } // Call this method from the main activity to shut down the connection. public void cancel() { try { mmSocket.close(); } catch (IOException e) { Log.e(TAG, "Could not close the connect socket", e); }}}}Copy the code

Low power Bluetooth development for Android