Android提高之Android手机与BLE终端通信

时间:2022-01-26 07:00:44

最近穿戴设备发展得很火,把相关技术也带旺了,其中一项是ble(bluetooth low energy)。ble是蓝牙4.0的核心profile,主打功能是快速搜索,快速连接,超低功耗保持连接和传输数据,弱点是数据传输速率低,由于ble的低功耗特点,因此普遍用于穿戴设备。android 4.3才开始支持ble api,所以请各位客官把本文代码运行在蓝牙4.0和android 4.3及其以上的系统,另外本文所用的ble终端是一个蓝牙4.0的串口蓝牙模块。

注:笔者的i9100刷了4.4系统后,竟然也能跟ble蓝牙模块通信

ble分为三部分service、characteristic、descriptor,这三部分都由uuid作为唯一标示符。一个蓝牙4.0的终端可以包含多个service,一个service可以包含多个characteristic,一个characteristic包含一个value和多个descriptor,一个descriptor包含一个value。一般来说,characteristic是手机与ble终端交换数据的关键,characteristic有较多的跟权限相关的字段,例如permission和property,而其中最常用的是property,本文所用的ble蓝牙模块竟然没有标准的characteristic的permission。characteristic的property可以通过位运算符组合来设置读写属性,例如read|write、read|write_no_response|notify,因此读取property后要分解成所用的组合(本文代码已含此分解方法)。

本文代码改自android 4.3 sample的bluetoothlegatt,把冗余代码去掉,获取的ble设备信息都通过log,还有一些必要的读写蓝牙方法,应该算是简化到大家一看就可以懂了。本文完整代码可以点击此处本站下载

接下来贴出本文运行的结果,首先是连接ble设备后,枚举出设备所有service、characteristic、descriptor,并且手机会往characteristic uuid=0000ffe1-0000-1000-8000-00805f9b34fb写入“send data->”字符串,ble终端收到数据通过串口传到pc串口助手
04-21 18:28:25.465: e/devicescanactivity(12254): -->service type:primary
04-21 18:28:25.465: e/devicescanactivity(12254): -->includedservices size:0
04-21 18:28:25.465: e/devicescanactivity(12254): -->service uuid:00001800-0000-1000-8000-00805f9b34fb
04-21 18:28:25.465: e/devicescanactivity(12254): ---->char uuid:00002a00-0000-1000-8000-00805f9b34fb
04-21 18:28:25.465: e/devicescanactivity(12254): ---->char permission:unknow
04-21 18:28:25.465: e/devicescanactivity(12254): ---->char property:read
04-21 18:28:25.465: e/devicescanactivity(12254): ---->char uuid:00002a01-0000-1000-8000-00805f9b34fb
04-21 18:28:25.470: e/devicescanactivity(12254): ---->char permission:unknow
04-21 18:28:25.470: e/devicescanactivity(12254): ---->char property:read
04-21 18:28:25.470: e/devicescanactivity(12254): ---->char uuid:00002a02-0000-1000-8000-00805f9b34fb
04-21 18:28:25.470: e/devicescanactivity(12254): ---->char permission:unknow
04-21 18:28:25.470: e/devicescanactivity(12254): ---->char property:read|write|
04-21 18:28:25.470: e/devicescanactivity(12254): ---->char uuid:00002a03-0000-1000-8000-00805f9b34fb
04-21 18:28:25.470: e/devicescanactivity(12254): ---->char permission:unknow
04-21 18:28:25.475: e/devicescanactivity(12254): ---->char property:read|write|
04-21 18:28:25.475: e/devicescanactivity(12254): ---->char uuid:00002a04-0000-1000-8000-00805f9b34fb
04-21 18:28:25.475: e/devicescanactivity(12254): ---->char permission:unknow
04-21 18:28:25.475: e/devicescanactivity(12254): ---->char property:read
04-21 18:28:25.475: e/devicescanactivity(12254): -->service type:primary
04-21 18:28:25.475: e/devicescanactivity(12254): -->includedservices size:0
04-21 18:28:25.475: e/devicescanactivity(12254): -->service uuid:00001801-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: e/devicescanactivity(12254): ---->char uuid:00002a05-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: e/devicescanactivity(12254): ---->char permission:unknow
04-21 18:28:25.480: e/devicescanactivity(12254): ---->char property:indicate
04-21 18:28:25.480: e/devicescanactivity(12254): -------->desc uuid:00002902-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: e/devicescanactivity(12254): -------->desc permission:unknow
04-21 18:28:25.480: e/devicescanactivity(12254): -->service type:primary
04-21 18:28:25.480: e/devicescanactivity(12254): -->includedservices size:0
04-21 18:28:25.480: e/devicescanactivity(12254): -->service uuid:0000ffe0-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: e/devicescanactivity(12254): ---->char uuid:0000ffe1-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: e/devicescanactivity(12254): ---->char permission:unknow
04-21 18:28:25.480: e/devicescanactivity(12254): ---->char property:read|write_no_response|notify|
04-21 18:28:25.490: e/devicescanactivity(12254): -------->desc uuid:00002902-0000-1000-8000-00805f9b34fb
04-21 18:28:25.490: e/devicescanactivity(12254): -------->desc permission:unknow
04-21 18:28:25.490: e/devicescanactivity(12254): -------->desc uuid:00002901-0000-1000-8000-00805f9b34fb
04-21 18:28:25.490: e/devicescanactivity(12254): -------->desc permission:unknow
04-21 18:28:26.025: e/devicescanactivity(12254): oncharread ble device read 0000ffe1-0000-1000-8000-00805f9b34fb -> 00
这里红字是由bluetoothgattcallback的oncharacteristicread()回调而打出log

Android提高之Android手机与BLE终端通信

以下log是pc上的串口工具通过ble模块发送过来,由bluetoothgattcallback的 oncharacteristicchanged()打出log
04-21 18:30:18.260: e/devicescanactivity(12254): oncharwrite ble device write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:18.745: e/devicescanactivity(12254): oncharwrite ble device write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:19.085: e/devicescanactivity(12254): oncharwrite ble device write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:19.350: e/devicescanactivity(12254): oncharwrite ble device write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:19.605: e/devicescanactivity(12254): oncharwrite ble device write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:19.835: e/devicescanactivity(12254): oncharwrite ble device write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:20.055: e/devicescanactivity(12254): oncharwrite ble device write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:20.320: e/devicescanactivity(12254): oncharwrite ble device write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:20.510: e/devicescanactivity(12254): oncharwrite ble device write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:20.735: e/devicescanactivity(12254): oncharwrite ble device write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:21.000: e/devicescanactivity(12254): oncharwrite ble device write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone

接下来贴出本文核心代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
public class devicescanactivity extends listactivity {
 private final static string tag = devicescanactivity.class.getsimplename();
 private final static string uuid_key_data = "0000ffe1-0000-1000-8000-00805f9b34fb";
 
  private ledevicelistadapter mledevicelistadapter;
  /**搜索ble终端*/
  private bluetoothadapter mbluetoothadapter;
  /**读写ble终端*/
  private bluetoothleclass mble;
  private boolean mscanning;
  private handler mhandler;
 
  // stops scanning after 10 seconds.
  private static final long scan_period = 10000;
 
  @override
  public void oncreate(bundle savedinstancestate) {
    super.oncreate(savedinstancestate);
    getactionbar().settitle(r.string.title_devices);
    mhandler = new handler();
    // use this check to determine whether ble is supported on the device. then you can
    // selectively disable ble-related features.
    if (!getpackagemanager().hassystemfeature(packagemanager.feature_bluetooth_le)) {
      toast.maketext(this, r.string.ble_not_supported, toast.length_short).show();
      finish();
    }
    // initializes a bluetooth adapter. for api level 18 and above, get a reference to
    // bluetoothadapter through bluetoothmanager.
    final bluetoothmanager bluetoothmanager =
        (bluetoothmanager) getsystemservice(context.bluetooth_service);
    mbluetoothadapter = bluetoothmanager.getadapter();
    // checks if bluetooth is supported on the device.
    if (mbluetoothadapter == null) {
      toast.maketext(this, r.string.error_bluetooth_not_supported, toast.length_short).show();
      finish();
      return;
    }
    //开启蓝牙
    mbluetoothadapter.enable();
    mble = new bluetoothleclass(this);
    if (!mble.initialize()) {
      log.e(tag, "unable to initialize bluetooth");
      finish();
    }
    //发现ble终端的service时回调
    mble.setonservicediscoverlistener(monservicediscover);
    //收到ble终端数据交互的事件
    mble.setondataavailablelistener(mondataavailable);
  }
  @override
  protected void onresume() {
    super.onresume();
 
    // initializes list view adapter.
    mledevicelistadapter = new ledevicelistadapter(this);
    setlistadapter(mledevicelistadapter);
    scanledevice(true);
  }
  @override
  protected void onpause() {
    super.onpause();
    scanledevice(false);
    mledevicelistadapter.clear();
    mble.disconnect();
  }
  @override
  protected void onstop() {
    super.onstop();
    mble.close();
  }
  @override
  protected void onlistitemclick(listview l, view v, int position, long id) {
    final bluetoothdevice device = mledevicelistadapter.getdevice(position);
    if (device == null) return;
    if (mscanning) {
      mbluetoothadapter.stoplescan(mlescancallback);
      mscanning = false;
    }
    mble.connect(device.getaddress());
  }
  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);
          invalidateoptionsmenu();
        }
      }, scan_period);
      mscanning = true;
      mbluetoothadapter.startlescan(mlescancallback);
    } else {
      mscanning = false;
      mbluetoothadapter.stoplescan(mlescancallback);
    }
    invalidateoptionsmenu();
  }
  /**
   * 搜索到ble终端服务的事件
   */
  private bluetoothleclass.onservicediscoverlistener monservicediscover = new onservicediscoverlistener(){
 
 @override
 public void onservicediscover(bluetoothgatt gatt) {
  displaygattservices(mble.getsupportedgattservices());
 }
  };
  /**
   * 收到ble终端数据交互的事件
   */
  private bluetoothleclass.ondataavailablelistener mondataavailable = new ondataavailablelistener(){
   /**
   * ble终端数据被读的事件
   */
 @override
 public void oncharacteristicread(bluetoothgatt gatt,
  bluetoothgattcharacteristic characteristic, int status) {
  if (status == bluetoothgatt.gatt_success)
  log.e(tag,"oncharread "+gatt.getdevice().getname()
   +" read "
   +characteristic.getuuid().tostring()
   +" -> "
   +utils.bytestohexstring(characteristic.getvalue()));
 }
   /**
   * 收到ble终端写入数据回调
   */
 @override
 public void oncharacteristicwrite(bluetoothgatt gatt,
  bluetoothgattcharacteristic characteristic) {
  log.e(tag,"oncharwrite "+gatt.getdevice().getname()
   +" write "
   +characteristic.getuuid().tostring()
   +" -> "
   +new string(characteristic.getvalue()));
 }
  };
  // device scan callback.
  private bluetoothadapter.lescancallback mlescancallback =
      new bluetoothadapter.lescancallback() {
    @override
    public void onlescan(final bluetoothdevice device, int rssi, byte[] scanrecord) {
      runonuithread(new runnable() {
        @override
        public void run() {
          mledevicelistadapter.adddevice(device);
          mledevicelistadapter.notifydatasetchanged();
        }
      });
    }
  };
  private void displaygattservices(list<bluetoothgattservice> gattservices) {
    if (gattservices == null) return;
    for (bluetoothgattservice gattservice : gattservices) {
     //-----service的字段信息-----//
     int type = gattservice.gettype();
      log.e(tag,"-->service type:"+utils.getservicetype(type));
      log.e(tag,"-->includedservices size:"+gattservice.getincludedservices().size());
      log.e(tag,"-->service uuid:"+gattservice.getuuid());
      
      //-----characteristics的字段信息-----//
      list<bluetoothgattcharacteristic> gattcharacteristics =gattservice.getcharacteristics();
      for (final bluetoothgattcharacteristic gattcharacteristic: gattcharacteristics) {
        log.e(tag,"---->char uuid:"+gattcharacteristic.getuuid());
        int permission = gattcharacteristic.getpermissions();
        log.e(tag,"---->char permission:"+utils.getcharpermission(permission));
        int property = gattcharacteristic.getproperties();
        log.e(tag,"---->char property:"+utils.getcharpropertie(property));
        byte[] data = gattcharacteristic.getvalue();
     if (data != null && data.length > 0) {
      log.e(tag,"---->char value:"+new string(data));
     }
     //uuid_key_data是可以跟蓝牙模块串口通信的characteristic
     if(gattcharacteristic.getuuid().tostring().equals(uuid_key_data)){     
      //测试读取当前characteristic数据,会触发mondataavailable.oncharacteristicread()
      mhandler.postdelayed(new runnable() {
            @override
            public void run() {
             mble.readcharacteristic(gattcharacteristic);
            }
          }, 500);
      //接受characteristic被写的通知,收到蓝牙模块的数据后会触发mondataavailable.oncharacteristicwrite()
      mble.setcharacteristicnotification(gattcharacteristic, true);
      //设置数据内容
      gattcharacteristic.setvalue("send data->");
      //往蓝牙模块写入数据
      mble.writecharacteristic(gattcharacteristic);
     }
     //-----descriptors的字段信息-----//
  list<bluetoothgattdescriptor> gattdescriptors = gattcharacteristic.getdescriptors();
  for (bluetoothgattdescriptor gattdescriptor : gattdescriptors) {
   log.e(tag, "-------->desc uuid:" + gattdescriptor.getuuid());
   int descpermission = gattdescriptor.getpermissions();
   log.e(tag,"-------->desc permission:"+ utils.getdescpermission(descpermission));
   byte[] desdata = gattdescriptor.getvalue();
   if (desdata != null && desdata.length > 0) {
   log.e(tag, "-------->desc value:"+ new string(desdata));
   }
      }
      }
    }//
  }
}

感兴趣的读者可以动手测试一下代码的运行情况,希望能对大家的android项目开发有所帮助。