ESP32S蓝牙08
继续ESP32S的BLE蓝牙学习。在上一篇中,我们完成了APP对BLE蓝牙作为客户端,是如何连接服务器端,并且与服务器端的蓝牙设备进行通讯。今天我们继续完成手机APP作为服务器端时,要怎样广播服务消息,如何与连接的客户端进行通讯的。
需要知道的是,安卓是从4.3(API 18版本)才开始支持蓝牙BLE客户端,而从5.0(API 21版本)以后才开始支持BLE服务器端的。看来我之前的升级Eclipse以及Android SDK(API 27版本)是非常及时的,也才有这一系列的蓝牙学习。
我们要做的就是BLEServer,和前一篇中的BLE客户端程序进行通讯,如上图所示,两部手机分别安装了客户端和服务器程序,然后就可以互相收发消息了。
还是先上源码吧,里面有注释。
这个是主程序 MainActivity. java
package com.example.bleserver;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelUuid;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattServer;
import android.bluetooth.BluetoothGattServerCallback;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.AdvertiseCallback;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertiseSettings;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.content.Context;
import android.content.Intent;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.util.UUID;
@SuppressLint("NewApi")
public class MainActivity extends AppCompatActivity {
//public final static UUID CLIENT_CHARACTERISTIC_CONFIG = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); //通用蓝牙的标志
private UUID mServiceUUID = UUID.fromString("6E400001-B5A3-F393-E0A9-E50E24DCCA9E");
private UUID mReadUUID = UUID.fromString("6E400002-B5A3-F393-E0A9-E50E24DCCA9E");
private UUID mWriteUUID = UUID.fromString("6E400003-B5A3-F393-E0A9-E50E24DCCA9E");
private Button scan_button, send_button, discon_button;
private TextView msgstr;
private EditText msgstr02;
private BluetoothAdapter bleadapter;
private BluetoothLeAdvertiser mBluetoothLeAdvertiser;
private BluetoothGatt bluetoothGatt;
private BluetoothGattServer gattServer;
private BluetoothGattService bluetoothGattServices;
private BluetoothDevice bluetoothDevice;
private BluetoothGattCharacteristic character_read, character_write;
private int inval=0;
private boolean connected = false;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
msgstr02.setText(msgstr02.getText().toString() + "in: " + (String) msg.obj);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
msgstr = (TextView) findViewById(R.id.txt_msg);
msgstr02 = (EditText) findViewById(R.id.txt_msg02);
scan_button = (Button) findViewById(R.id.btn_scan);
send_button = (Button) findViewById(R.id.btn_send);
discon_button = (Button) findViewById(R.id.btn_discon);
msgstr02.setFocusable(false);
//初始化ble设配器
BluetoothManager manager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
bleadapter = manager.getAdapter();
//判断蓝牙是否开启,若是关闭则请求打开蓝牙
if (bleadapter == null || !bleadapter.isEnabled()) {
//方式一:请求打开蓝牙
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent, 1);
}
// 获取蓝牙ble广播对象
assert bleadapter != null;
mBluetoothLeAdvertiser = bleadapter.getBluetoothLeAdvertiser();
gattServer = manager.openGattServer(this, bluetoothGattServerCallback);
//BLE服务的初始化配置,很重要
//配置的顺序是 服务service 特征(通道)Characteristic 属性Descriptor
//配置服务名、 主服务类型
BluetoothGattService service=new BluetoothGattService(mServiceUUID, BluetoothGattService.SERVICE_TYPE_PRIMARY);
//配置一个读的特征 (可读、可写、可侦听)
character_read=new BluetoothGattCharacteristic(mReadUUID,
BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_WRITE | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_READ | BluetoothGattCharacteristic.PERMISSION_WRITE);
BluetoothGattDescriptor descriptor=new BluetoothGattDescriptor(mReadUUID,
BluetoothGattCharacteristic.PERMISSION_READ | BluetoothGattCharacteristic.PERMISSION_WRITE);
character_read.addDescriptor(descriptor);
service.addCharacteristic(character_read);
//配置一个写的特征 (可写)
character_write=new BluetoothGattCharacteristic(mWriteUUID, BluetoothGattCharacteristic.PROPERTY_WRITE, BluetoothGattCharacteristic.PERMISSION_WRITE);
service.addCharacteristic(character_write);
//添加到
gattServer.addService(service);
//开启广播按钮
scan_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//开启广播需要传入三个参数: 基本设置 附加数据 回调函数
AdvertiseSettings settings = new AdvertiseSettings.Builder()
.setConnectable(true) //是否被连接
.setTimeout(0) //超时时间
.build();
//广播数据设置
AdvertiseData advertiseData = new AdvertiseData.Builder()
.setIncludeDeviceName(true) //是否在广播中携带设备的名称
.setIncludeTxPowerLevel(true) //是否在广播中携带信号强度
.build();
//扫描回应的广播设置
AdvertiseData scanResponseData = new AdvertiseData.Builder()
.setIncludeTxPowerLevel(true) //是否在广播中携带设备的名称
.addServiceData(new ParcelUuid(mServiceUUID), new byte[]{1,2}) //在scanrecord中添加的数据
.build();
//设置BLE设备的名称
bleadapter.setName("BLEServer");
//开启广播
mBluetoothLeAdvertiser.startAdvertising(settings, advertiseData, scanResponseData, mAdvertiseCallback);
}
});
//发送消息按钮
send_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(connected == true) {
inval += 1;
String str = "hello" + inval + "\n\r";
character_write.setValue(str);
gattServer.notifyCharacteristicChanged(bluetoothDevice, character_write, false);
msgstr02.setText(msgstr02.getText().toString() + "ou: " + str);
}
}
});
//停止广播按钮
discon_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (connected == true) {
mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
connected = false;
}
}
});
}
private AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() {
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
msgstr.setText("开启广播成功" + "\n");
}
@Override
public void onStartFailure(int errorCode) {
super.onStartFailure(errorCode);
msgstr.setText("开启广播失败" + "\n");
}
};
//广播服务管理状态回调
private BluetoothGattServerCallback bluetoothGattServerCallback=new BluetoothGattServerCallback() {
//连接状态的回调
@Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
super.onConnectionStateChange(device, status, newState);
if(newState == BluetoothProfile.STATE_CONNECTED) {
bluetoothDevice = device;
msgstr.setText("已连接到 " + device.getName() + "\n");
connected = true;
}
if(newState == BluetoothProfile.STATE_DISCONNECTED) {
msgstr.setText("连接已断开 " + "\n");
msgstr02.setText("");
}
}
@Override
public void onServiceAdded(int status,BluetoothGattService service){
super.onServiceAdded(status,service);
msgstr.setText("添加服务成功");
}
//获取接收,接收的数据为参数中的value
@Override
public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
gattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
Message mesg = new Message();
mesg.what = 1;
mesg.obj = new String(value);
MainActivity.this.handler.sendMessage(mesg);
}
@Override
public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {
super.onDescriptorReadRequest(device, requestId, offset, descriptor);
}
@Override
public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
}
@Override
public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value);
}
};
}
这个是界面设计 activity_main. xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<Button
android:id="@+id/btn_scan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="开始广播" />
<Button
android:id="@+id/btn_send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="发送消息" />
<Button
android:id="@+id/btn_discon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="停止广播" />
<TextView
android:id="@+id/txt_msg"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<EditText
android:id="@+id/txt_msg02"
android:minLines="12"
android:gravity="top"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
这个是安卓的版本号以及权限申请 在AndroidManifest. xml
<uses-sdk
android:minSdkVersion="11"
android:targetSdkVersion="21" />
<!-- 添加蓝牙权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-feature android:name="android.hardware.location.gps" />
<!-- Android6.0 蓝牙扫描才须要-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
整体回顾一下:BLE蓝牙服务器的配置是关键的,配置的顺序是: 服务service——特征(通道)Characteristic——属性Descriptor。
在这里,我还是沿用蓝牙小车ESP32S的广播,在最这里使用指定的UUID方式,为这个服务器设置了一个读通道和一个写通道。(UUID要和前一篇的客户端APP中的匹配,但是特别要注意的是,服务器端的读和写与客户端是相反的。也就是服务器端的读(输入)就是客户端的写(输出), 相反服务器端的写(输出)就是客户端的读(输入))
mServiceUUID = UUID.fromString("6E400001-B5A3-F393-E0A9-E50E24DCCA9E");
mReadUUID = UUID.fromString("6E400002-B5A3-F393-E0A9-E50E24DCCA9E");
mWriteUUID = UUID.fromString("6E400003-B5A3-F393-E0A9-E50E24DCCA9E");
在服务器端,我们要把读的通道设置为(可读、可写、可侦听),这样才能收到从客户端发过来的消息。写的通道比较简单,只要设置成(可写)就可以了。