峰澜未语 發表於 2022-3-28 16:57:00

Android BLE 蓝牙开发——扫码枪基于BLESSED

<p><strong>  一、蓝牙模式HID与BLE</strong></p>
<p>  当扫码枪与手机连接时,通常采用的是<strong>蓝牙HID</strong>(Human Interface Device)模式。本质上是一个把扫码枪作为一个硬件键盘,按照键盘协议把扫码后的结果逐个输入到对应的控件上。</p>
<p>  <strong>优点:</strong>无需开发集成,配对就可以立即作为键盘输入使用。可以使用输入框等组件直接接收扫码结果。</p>
<p>  <strong>缺点:</strong>对非数字支持不佳,与输入法相关,在某些时候会触发英文联想-_-||,与虚拟键盘会发生冲突,连接扫码枪时需要切换键盘输入法输入。</p>
<p>  而我们要扫描的标签,不仅有英文,特殊符号,还有中文,因此以HID模式接入的蓝牙扫码枪,最终是不能满足要求的。于是重新选型了支持BLE模式的扫码枪。</p>
<p>  <strong>BLE模式扫码枪</strong></p>
<p>  <strong>优点:</strong>兼容性好,遵循蓝牙协议,与键盘输入法无关。更底层,直接返回原始二进制数据流,方便判定编码以及进行字符编码转换。</p>
<p><strong>  缺点:</strong>需要进行原生开发, 处理设备扫描,连接,数据读写等操作。</p>
<p>  二、<strong>BLE协议白话</strong></p>
<p>  好在有github,其中的&nbsp;BLESSED for Android - BLE made easy 项目,就是一个致力于简化android上BLE开发工作的开源库。但在撸代码前还是要简单理解下BLE协议的主要概念。</p>
<p>  较为重要的两个东西是<strong>Service(服务)</strong> 与<strong>Characteristic</strong>(<strong>特性</strong>,译为功能可能更好理解),简而言之,一个设备可以提供多个服务,每个服务可以提供多个特性功能,每个服务及特性对应一个<strong>UUID</strong>。</p>
<p>  与设备的通信通过功能进行,每个功能通过<strong>Properties(属性)</strong>表明该特性支持<strong>读,写或者通知</strong>。</p>
<p>  为了便于理解BLE协议,推荐下载一个叫做“<strong>BLE调试助手</strong>”的APP。下面是APP的截图。</p>
<p>  <img src="https://img2022.cnblogs.com/blog/2753310/202203/2753310-20220328154404520-2115960180.jpg">&nbsp;&nbsp;<img src="https://img2022.cnblogs.com/blog/2753310/202203/2753310-20220328154432830-640368046.jpg">&nbsp;&nbsp;<img src="https://img2022.cnblogs.com/blog/2753310/202203/2753310-20220328154518010-825592698.jpg"></p>
<p>&nbsp; &nbsp; 截图演示了如何从一个支持BLE协议的设备中读取电量,不需要提前配对,打开APP扫描到对应设备后,点击Connect, 随后列出的就是一堆上面说的Service(服务),能够显示服务名称的如“Battery Service”,是根据UUID的约定取得的。</p>
<p>  如电池服务为0x180F。点开服务后是Characteristic,其中的Battery Level(也是UUID约定 0x2A19)的Properties为 READ NOTIFY,表明该特性支持读取和通知。</p>
<p>  点击那个下箭头,点击读取,显示出返回数据为0x5D(十进制估计九十多:-0)。</p>
<p>  电池服务是一个在协议中约定的标准服务,但扫码枪的似乎不是,我们还需要找到扫码后,是通过哪个Service的哪个Characteristic进行通知的,通过这个工具APP也不难找,<strong>注意,要打开那个接受通知数据。</strong></p>
<p><strong>  </strong>扫码后会有数据显示,找到之后就可以开始编码了。</p>
<p><strong>  三、第三方库&nbsp; BLESSED for Android&nbsp;的使用</strong></p>
<p>&nbsp;  https://github.com/weliem/blessed-android</p>
<p>  下面进入具体的撸代码环节</p>
<p>  安装 gradle file加入</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">allprojects {
      repositories {
                ...
                maven { url </span>'https://jitpack.io'<span style="color: rgba(0, 0, 0, 1)"> }
      }
}

dependencies {
      implementation </span>'com.github.weliem:blessed-android:2.0.6'<span style="color: rgba(0, 0, 0, 1)">
}</span></pre>
</div>
<p>  扫描设备</p>
<div class="cnblogs_code">
<pre>BluetoothCentralManager central = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> BluetoothCentralManager(AppContext.baseAppContext,
                                                            <strong><span style="color: rgba(128, 0, 0, 1)">bluetoothCentralManagerCallback</span></strong>,
                                                            </span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Handler(Looper.getMainLooper()));
central.scanForPeripherals();</span></pre>
</div>
<p>  <span style="color: rgba(128, 0, 0, 1)">bluetoothCentralManagerCallback</span>是扫描回调方法,重要的有下面三个</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(51, 153, 102, 1)">//发现了一个设备</span><br>@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> onDiscoveredPeripheral(BluetoothPeripheral peripheral, ScanResult scanResult)
<span style="color: rgba(51, 153, 102, 1)">//连接设备</span><br>@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> onConnectedPeripheral(BluetoothPeripheral peripheral)</pre>
<pre><span style="color: rgba(51, 153, 102, 1)">//设备断开</span><br>@Override<br><span style="color: rgba(51, 102, 255, 1)">public void</span> onDisconnectedPeripheral(BluetoothPeripheral peripheral, HciStatus status)</pre>
</div>
<p>  发现设备后onDiscoveredPeripheral,连接设备,停止扫描 。<strong><span style="color: rgba(128, 0, 0, 1)">bluetoothPeripheralCallback</span> 为设备回调,用于接受通知</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">central.autoConnectPeripheral(peripheral, <strong><span style="color: rgba(128, 0, 0, 1)">bluetoothPeripheralCallback</span></strong>);
central.stopScan();</span></pre>
</div>
<p>  onConnectedPeripheral连接后查询提供的服务及特性</p>
<div class="cnblogs_code">
<pre>Log.i("BLE","onConnectedPeripheral"<span style="color: rgba(0, 0, 0, 1)">);
List</span>&lt;BluetoothGattService&gt; serviceList =<span style="color: rgba(0, 0, 0, 1)">peripheral.getServices();
</span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = 0; i &lt; serviceList.size(); i++<span style="color: rgba(0, 0, 0, 1)">) {
    Log.i(</span>"BLE", "Service:" +<span style="color: rgba(0, 0, 0, 1)"> serviceList.get(i).getUuid());
    </span><span style="color: rgba(0, 0, 255, 1)">if</span>(serviceList.get(i).getUuid().toString().equals("6e400001-b5a3-f393-e0a9-e50e24dcca9e"<span style="color: rgba(0, 0, 0, 1)">)){
      List</span>&lt;BluetoothGattCharacteristic&gt; list=<span style="color: rgba(0, 0, 0, 1)">serviceList.get(i).getCharacteristics();
      </span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> j = 0; j &lt; list.size(); j++<span style="color: rgba(0, 0, 0, 1)">) {
            Log.i(</span>"BLE", "Characteristic:" +<span style="color: rgba(0, 0, 0, 1)"> list.get(j).getUuid());
      }
    }
}</span></pre>
</div>
<p>  onConnectedPeripheral后,对特性开启通知,接受扫码结果,服务的和特性的UUID,需要对应填写,扫码结果是以通知信息返回的。</p>
<div class="cnblogs_code">
<pre>BluetoothGattCharacteristic currentTimeCharacteristic =<span style="color: rgba(0, 0, 0, 1)"> peripheral.getCharacteristic(<span style="color: rgba(128, 0, 0, 1)">SERVICE_UUID</span>, <span style="color: rgba(128, 0, 0, 1)">CURRENT_TIME_CHARACTERISTIC_UUID</span>);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (currentTimeCharacteristic != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {<br>   <span style="color: rgba(51, 153, 102, 1)">//开启通知</span>
    peripheral.setNotify(currentTimeCharacteristic, </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">与设备通信需要创建绑定</span>
<span style="color: rgba(0, 0, 255, 1)">boolean</span> bret=peripheral.createBond();</pre>
</div>
<p>  断开重连可以在onDisconnectedPeripheral中处理</p>
<p><strong>  接收扫码结果 在设备回调类 bluetoothPeripheralCallback中的下列方法处理。其中value为扫到的二维码值&nbsp;</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> onCharacteristicUpdate(@NonNull BluetoothPeripheral peripheral, @NonNull <span style="color: rgba(0, 0, 255, 1)">byte</span>[] value, @NonNull BluetoothGattCharacteristic characteristic, @NonNull GattStatus status)</pre>
</div>
<p>  <strong>另:</strong></p>
<ul>
<li><strong>  </strong>中文编码通常为GBK或者UTF8,需要猜测判断,最后附了个工具函数</li>
<li>  因为BLE默认数据包较短,对大量数据,会拆分多次发送,onCharacteristicUpdate会被调用多次,需要将value拼接后,进行字符编码判断处理。  </li>
</ul>
<div class="cnblogs_code"><img id="code_img_closed_8d11e24e-058e-4b71-aa81-c25f9c301252" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_8d11e24e-058e-4b71-aa81-c25f9c301252" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_8d11e24e-058e-4b71-aa81-c25f9c301252" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> Boolean isUtf8(<span style="color: rgba(0, 0, 255, 1)">byte</span><span style="color: rgba(0, 0, 0, 1)">[] buffer) {
      </span><span style="color: rgba(0, 0, 255, 1)">boolean</span> isUtf8 = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(0, 0, 255, 1)">int</span> end =<span style="color: rgba(0, 0, 0, 1)"> buffer.length;
      </span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = 0; i &lt; end; i++<span style="color: rgba(0, 0, 0, 1)">) {
            </span><span style="color: rgba(0, 0, 255, 1)">byte</span> temp =<span style="color: rgba(0, 0, 0, 1)"> buffer;
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> ((temp &amp; 0x80) == 0) {<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 0xxxxxxx</span>
                <span style="color: rgba(0, 0, 255, 1)">continue</span><span style="color: rgba(0, 0, 0, 1)">;
            } </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> ((temp &amp; 0xC0) == 0xC0 &amp;&amp; (temp &amp; 0x20) == 0) {<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 110xxxxx 10xxxxxx</span>
                <span style="color: rgba(0, 0, 255, 1)">if</span> (i + 1 &lt; end &amp;&amp; (buffer &amp; 0x80) == 0x80 &amp;&amp; (buffer &amp; 0x40) == 0<span style="color: rgba(0, 0, 0, 1)">) {
                  i </span>= i + 1<span style="color: rgba(0, 0, 0, 1)">;
                  </span><span style="color: rgba(0, 0, 255, 1)">continue</span><span style="color: rgba(0, 0, 0, 1)">;
                }
            } </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> ((temp &amp; 0xE0) == 0xE0 &amp;&amp; (temp &amp; 0x10) == 0) {<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 1110xxxx 10xxxxxx 10xxxxxx</span>
                <span style="color: rgba(0, 0, 255, 1)">if</span> (i + 2 &lt; end &amp;&amp; (buffer &amp; 0x80) == 0x80 &amp;&amp; (buffer &amp; 0x40) == 0
                        &amp;&amp; (buffer &amp; 0x80) == 0x80 &amp;&amp; (buffer &amp; 0x40) == 0<span style="color: rgba(0, 0, 0, 1)">) {
                  i </span>= i + 2<span style="color: rgba(0, 0, 0, 1)">;
                  </span><span style="color: rgba(0, 0, 255, 1)">continue</span><span style="color: rgba(0, 0, 0, 1)">;
                }
            } </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> ((temp &amp; 0xF0) == 0xF0 &amp;&amp; (temp &amp; 0x08) == 0) {<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx</span>
                <span style="color: rgba(0, 0, 255, 1)">if</span> (i + 3 &lt; end &amp;&amp; (buffer &amp; 0x80) == 0x80 &amp;&amp; (buffer &amp; 0x40) == 0
                        &amp;&amp; (buffer &amp; 0x80) == 0x80 &amp;&amp; (buffer &amp; 0x40) == 0
                        &amp;&amp; (buffer &amp; 0x80) == 0x80 &amp;&amp; (buffer &amp; 0x40) == 0<span style="color: rgba(0, 0, 0, 1)">) {
                  i </span>= i + 3<span style="color: rgba(0, 0, 0, 1)">;
                  </span><span style="color: rgba(0, 0, 255, 1)">continue</span><span style="color: rgba(0, 0, 0, 1)">;
                }
            }
            isUtf8 </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
            </span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
      }
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> isUtf8;
    }</span></pre>
</div>
<span class="cnblogs_code_collapse">isUtf8</span>  </div>

</div>
<div id="MySignature" role="contentinfo">
    <p style="color:#98430e;text-indent:0;font-weight: bold;">本文来自博客园,作者:锅叔<br>转载请注明原文链接:https://www.cnblogs.com/uncleguo/p/16067271.html</p><br><br>
来源:https://www.cnblogs.com/uncleguo/p/16067271.html
頁: [1]
查看完整版本: Android BLE 蓝牙开发——扫码枪基于BLESSED