主頁 >  其他 > android專題-藍牙掃描、連接、讀寫

android專題-藍牙掃描、連接、讀寫

2021-02-05 16:28:03 其他

android專題-藍牙掃描、連接、讀寫

概念

外圍設備

可以被其他藍牙設備連接的外部藍牙設備,不斷廣播自身的藍牙名及其資料,如小米手環、共享單車、藍牙體重秤

中央設備

可以搜索并連接周邊的外圍設備,并與之進行資料讀寫通訊,如手機

日常生活中常見的場景是手機app通過藍牙開啟共享單車,手機app通過藍牙獲取藍牙體重秤的體重結果,這時候共享單車、藍牙體重秤就稱為外圍設備,而手機就稱為中央設備

經典藍牙BT

泛指支持藍牙協議在4.0以下的模塊,一般用于資料量比較大的傳輸,如:語音、音樂等較高資料量的傳輸,經典藍牙模塊又可細分為:傳統藍牙和高速藍牙模塊,傳統藍牙模塊在2004年推出,主要代表是支持藍牙2.1協議的模塊,在智能手機爆發的時期得到了廣泛的使用,高速藍牙模塊在2009年推出,速率提高到約24Mbps,傳輸速率是經典藍牙的八倍,可以輕松的應用于錄像機到電視、PC到PMP、UMPC到列印機之間的資料傳輸,

低功耗藍牙BLE

是指支持藍牙協議4.0或者以上的模塊,也被稱為BLE模塊,最大的特點就是成本和功耗的降低,可以應用于實時性要求較高的產品當中,比如:智能家居類(藍牙鎖、藍牙燈)、傳感設備的資料發送(血壓計、溫度傳感器)、消費類電子(電子煙、遙控玩具)等,

目前市面上大部分的藍牙都是4.0以上的低功耗藍牙,經典藍牙已經很少見到了,

通訊程序

一個外圍設備可以發布多個服務service,每個服務可以包含多個特征值characteristic,每個特征值都有他的屬性,例如長度(size),權限(permission),值(value),描述(descriptor),讀寫通訊都是通過Characteristic進行的,
每個service、characteristic都含有一個對應的UUID,通過和外圍設備藍牙約定UUID來進行讀寫通訊,

整個通訊流程為:

<style>#mermaid-svg-8yCFZq3U1nRgch8O .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-8yCFZq3U1nRgch8O .label text{fill:#333}#mermaid-svg-8yCFZq3U1nRgch8O .node rect,#mermaid-svg-8yCFZq3U1nRgch8O .node circle,#mermaid-svg-8yCFZq3U1nRgch8O .node ellipse,#mermaid-svg-8yCFZq3U1nRgch8O .node polygon,#mermaid-svg-8yCFZq3U1nRgch8O .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-8yCFZq3U1nRgch8O .node .label{text-align:center;fill:#333}#mermaid-svg-8yCFZq3U1nRgch8O .node.clickable{cursor:pointer}#mermaid-svg-8yCFZq3U1nRgch8O .arrowheadPath{fill:#333}#mermaid-svg-8yCFZq3U1nRgch8O .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-8yCFZq3U1nRgch8O .flowchart-link{stroke:#333;fill:none}#mermaid-svg-8yCFZq3U1nRgch8O .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-8yCFZq3U1nRgch8O .edgeLabel rect{opacity:0.9}#mermaid-svg-8yCFZq3U1nRgch8O .edgeLabel span{color:#333}#mermaid-svg-8yCFZq3U1nRgch8O .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-8yCFZq3U1nRgch8O .cluster text{fill:#333}#mermaid-svg-8yCFZq3U1nRgch8O div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-8yCFZq3U1nRgch8O .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-8yCFZq3U1nRgch8O text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-8yCFZq3U1nRgch8O .actor-line{stroke:grey}#mermaid-svg-8yCFZq3U1nRgch8O .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-8yCFZq3U1nRgch8O .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-8yCFZq3U1nRgch8O #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-8yCFZq3U1nRgch8O .sequenceNumber{fill:#fff}#mermaid-svg-8yCFZq3U1nRgch8O #sequencenumber{fill:#333}#mermaid-svg-8yCFZq3U1nRgch8O #crosshead path{fill:#333;stroke:#333}#mermaid-svg-8yCFZq3U1nRgch8O .messageText{fill:#333;stroke:#333}#mermaid-svg-8yCFZq3U1nRgch8O .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-8yCFZq3U1nRgch8O .labelText,#mermaid-svg-8yCFZq3U1nRgch8O .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-8yCFZq3U1nRgch8O .loopText,#mermaid-svg-8yCFZq3U1nRgch8O .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-8yCFZq3U1nRgch8O .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-8yCFZq3U1nRgch8O .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-8yCFZq3U1nRgch8O .noteText,#mermaid-svg-8yCFZq3U1nRgch8O .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-8yCFZq3U1nRgch8O .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-8yCFZq3U1nRgch8O .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-8yCFZq3U1nRgch8O .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-8yCFZq3U1nRgch8O .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8yCFZq3U1nRgch8O .section{stroke:none;opacity:0.2}#mermaid-svg-8yCFZq3U1nRgch8O .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-8yCFZq3U1nRgch8O .section2{fill:#fff400}#mermaid-svg-8yCFZq3U1nRgch8O .section1,#mermaid-svg-8yCFZq3U1nRgch8O .section3{fill:#fff;opacity:0.2}#mermaid-svg-8yCFZq3U1nRgch8O .sectionTitle0{fill:#333}#mermaid-svg-8yCFZq3U1nRgch8O .sectionTitle1{fill:#333}#mermaid-svg-8yCFZq3U1nRgch8O .sectionTitle2{fill:#333}#mermaid-svg-8yCFZq3U1nRgch8O .sectionTitle3{fill:#333}#mermaid-svg-8yCFZq3U1nRgch8O .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8yCFZq3U1nRgch8O .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-8yCFZq3U1nRgch8O .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8yCFZq3U1nRgch8O .grid path{stroke-width:0}#mermaid-svg-8yCFZq3U1nRgch8O .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-8yCFZq3U1nRgch8O .task{stroke-width:2}#mermaid-svg-8yCFZq3U1nRgch8O .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8yCFZq3U1nRgch8O .taskText:not([font-size]){font-size:11px}#mermaid-svg-8yCFZq3U1nRgch8O .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8yCFZq3U1nRgch8O .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-8yCFZq3U1nRgch8O .task.clickable{cursor:pointer}#mermaid-svg-8yCFZq3U1nRgch8O .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-8yCFZq3U1nRgch8O .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-8yCFZq3U1nRgch8O .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-8yCFZq3U1nRgch8O .taskText0,#mermaid-svg-8yCFZq3U1nRgch8O .taskText1,#mermaid-svg-8yCFZq3U1nRgch8O .taskText2,#mermaid-svg-8yCFZq3U1nRgch8O .taskText3{fill:#fff}#mermaid-svg-8yCFZq3U1nRgch8O .task0,#mermaid-svg-8yCFZq3U1nRgch8O .task1,#mermaid-svg-8yCFZq3U1nRgch8O .task2,#mermaid-svg-8yCFZq3U1nRgch8O .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-8yCFZq3U1nRgch8O .taskTextOutside0,#mermaid-svg-8yCFZq3U1nRgch8O .taskTextOutside2{fill:#000}#mermaid-svg-8yCFZq3U1nRgch8O .taskTextOutside1,#mermaid-svg-8yCFZq3U1nRgch8O .taskTextOutside3{fill:#000}#mermaid-svg-8yCFZq3U1nRgch8O .active0,#mermaid-svg-8yCFZq3U1nRgch8O .active1,#mermaid-svg-8yCFZq3U1nRgch8O .active2,#mermaid-svg-8yCFZq3U1nRgch8O .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-8yCFZq3U1nRgch8O .activeText0,#mermaid-svg-8yCFZq3U1nRgch8O .activeText1,#mermaid-svg-8yCFZq3U1nRgch8O .activeText2,#mermaid-svg-8yCFZq3U1nRgch8O .activeText3{fill:#000 !important}#mermaid-svg-8yCFZq3U1nRgch8O .done0,#mermaid-svg-8yCFZq3U1nRgch8O .done1,#mermaid-svg-8yCFZq3U1nRgch8O .done2,#mermaid-svg-8yCFZq3U1nRgch8O .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-8yCFZq3U1nRgch8O .doneText0,#mermaid-svg-8yCFZq3U1nRgch8O .doneText1,#mermaid-svg-8yCFZq3U1nRgch8O .doneText2,#mermaid-svg-8yCFZq3U1nRgch8O .doneText3{fill:#000 !important}#mermaid-svg-8yCFZq3U1nRgch8O .crit0,#mermaid-svg-8yCFZq3U1nRgch8O .crit1,#mermaid-svg-8yCFZq3U1nRgch8O .crit2,#mermaid-svg-8yCFZq3U1nRgch8O .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-8yCFZq3U1nRgch8O .activeCrit0,#mermaid-svg-8yCFZq3U1nRgch8O .activeCrit1,#mermaid-svg-8yCFZq3U1nRgch8O .activeCrit2,#mermaid-svg-8yCFZq3U1nRgch8O .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-8yCFZq3U1nRgch8O .doneCrit0,#mermaid-svg-8yCFZq3U1nRgch8O .doneCrit1,#mermaid-svg-8yCFZq3U1nRgch8O .doneCrit2,#mermaid-svg-8yCFZq3U1nRgch8O .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-8yCFZq3U1nRgch8O .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-8yCFZq3U1nRgch8O .milestoneText{font-style:italic}#mermaid-svg-8yCFZq3U1nRgch8O .doneCritText0,#mermaid-svg-8yCFZq3U1nRgch8O .doneCritText1,#mermaid-svg-8yCFZq3U1nRgch8O .doneCritText2,#mermaid-svg-8yCFZq3U1nRgch8O .doneCritText3{fill:#000 !important}#mermaid-svg-8yCFZq3U1nRgch8O .activeCritText0,#mermaid-svg-8yCFZq3U1nRgch8O .activeCritText1,#mermaid-svg-8yCFZq3U1nRgch8O .activeCritText2,#mermaid-svg-8yCFZq3U1nRgch8O .activeCritText3{fill:#000 !important}#mermaid-svg-8yCFZq3U1nRgch8O .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8yCFZq3U1nRgch8O g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-8yCFZq3U1nRgch8O g.classGroup text .title{font-weight:bolder}#mermaid-svg-8yCFZq3U1nRgch8O g.clickable{cursor:pointer}#mermaid-svg-8yCFZq3U1nRgch8O g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-8yCFZq3U1nRgch8O g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-8yCFZq3U1nRgch8O .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-8yCFZq3U1nRgch8O .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-8yCFZq3U1nRgch8O .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-8yCFZq3U1nRgch8O .dashed-line{stroke-dasharray:3}#mermaid-svg-8yCFZq3U1nRgch8O #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-8yCFZq3U1nRgch8O #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-8yCFZq3U1nRgch8O #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-8yCFZq3U1nRgch8O #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-8yCFZq3U1nRgch8O #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-8yCFZq3U1nRgch8O #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-8yCFZq3U1nRgch8O #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-8yCFZq3U1nRgch8O #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-8yCFZq3U1nRgch8O .commit-id,#mermaid-svg-8yCFZq3U1nRgch8O .commit-msg,#mermaid-svg-8yCFZq3U1nRgch8O .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8yCFZq3U1nRgch8O .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8yCFZq3U1nRgch8O .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8yCFZq3U1nRgch8O g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8yCFZq3U1nRgch8O g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-8yCFZq3U1nRgch8O g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-8yCFZq3U1nRgch8O g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-8yCFZq3U1nRgch8O g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-8yCFZq3U1nRgch8O g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-8yCFZq3U1nRgch8O .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-8yCFZq3U1nRgch8O .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-8yCFZq3U1nRgch8O .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-8yCFZq3U1nRgch8O .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-8yCFZq3U1nRgch8O .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-8yCFZq3U1nRgch8O .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-8yCFZq3U1nRgch8O .edgeLabel text{fill:#333}#mermaid-svg-8yCFZq3U1nRgch8O .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8yCFZq3U1nRgch8O .node circle.state-start{fill:black;stroke:black}#mermaid-svg-8yCFZq3U1nRgch8O .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-8yCFZq3U1nRgch8O #statediagram-barbEnd{fill:#9370db}#mermaid-svg-8yCFZq3U1nRgch8O .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-8yCFZq3U1nRgch8O .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-8yCFZq3U1nRgch8O .statediagram-state .divider{stroke:#9370db}#mermaid-svg-8yCFZq3U1nRgch8O .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-8yCFZq3U1nRgch8O .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-8yCFZq3U1nRgch8O .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-8yCFZq3U1nRgch8O .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-8yCFZq3U1nRgch8O .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-8yCFZq3U1nRgch8O .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-8yCFZq3U1nRgch8O .note-edge{stroke-dasharray:5}#mermaid-svg-8yCFZq3U1nRgch8O .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-8yCFZq3U1nRgch8O .error-icon{fill:#522}#mermaid-svg-8yCFZq3U1nRgch8O .error-text{fill:#522;stroke:#522}#mermaid-svg-8yCFZq3U1nRgch8O .edge-thickness-normal{stroke-width:2px}#mermaid-svg-8yCFZq3U1nRgch8O .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-8yCFZq3U1nRgch8O .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-8yCFZq3U1nRgch8O .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-8yCFZq3U1nRgch8O .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-8yCFZq3U1nRgch8O .marker{fill:#333}#mermaid-svg-8yCFZq3U1nRgch8O .marker.cross{stroke:#333} :root { --mermaid-font-family: "trebuchet ms", verdana, arial;}</style> <style>#mermaid-svg-8yCFZq3U1nRgch8O { color: rgba(0, 0, 0, 0.75); font: ; }</style>
創建藍牙實體
搜索掃描外圍設備
連接外圍設備
獲取外圍設備的服務service
獲取服務的特征characteristic
從外圍設備讀取資料,即讀資料
給外圍設備發送資料寫資料,即寫資料
斷開連接

常用業務API

1.判斷當前藍牙是否已經開啟,如果沒有開啟提示用戶開啟
2.實時掃描周邊藍牙,獲取藍牙名給用戶選擇
3.決議藍牙廣播資料處理業務
4.監聽外圍設備發送給app的資料,處理對應業務
5.app發送資料給外圍設備以處理業務

Android常用的第三方藍牙框架:okble、Android-BluetoothKit

以okble為例:

package com.wrs.project.module.app.common.bluetooth;
import android.content.Context;
import android.util.Log;

import com.a1anwang.okble.client.core.OKBLEDevice;
import com.a1anwang.okble.client.core.OKBLEDeviceImp;
import com.a1anwang.okble.client.core.OKBLEDeviceListener;
import com.a1anwang.okble.client.core.OKBLEOperation;
import com.a1anwang.okble.client.scan.BLEScanResult;
import com.a1anwang.okble.client.scan.DeviceScanCallBack;
import com.a1anwang.okble.client.scan.OKBLEScanManager;
import com.a1anwang.okble.common.OKBLECharacteristicModel;
import com.a1anwang.okble.common.OKBLEServiceModel;
import com.wrs.project.module.app.common.AppMgr;

import java.util.List;

public class Bluetooth implements DeviceScanCallBack, OKBLEDeviceListener{
    private OKBLEScanManager scanManager;
    private OKBLEDevice okbleDevice;// 當前連接的藍牙設備
    private OKBLECharacteristicModel writeCharacteristicModel; // 藍牙可寫的Characteristic
    private Context context = AppMgr.context;

    private String tag = "Bluetooth";

    public Bluetooth() {
        scanManager = new OKBLEScanManager(context);
        scanManager.setScanCallBack(this);
    }

    /**
     * 掃描到藍牙設備
     * @param device
     * @param rssi
     */
    @Override
    public void onBLEDeviceScan(BLEScanResult device, int rssi) {
        Log.e(tag, "掃描到藍牙設備:" + device.toString());
        String localName = device.getCompleteLocalName();
        if (null != localName && localName.startsWith("ABC-")) {
            stopScanBluetooth();
            connectBluetoothDevice(device);
        }
    }

    /**
     * 掃描失敗
     * @param code
     */
    @Override
    public void onFailed(int code) {

    }

    @Override
    public void onStartSuccess() {

    }

    /**
     * 藍牙是否已經開啟
     * @return
     */
    public boolean bluetoothIsEnable() {
        if (null != scanManager) {
            scanManager.bluetoothIsEnable();
        }
        return false;
    }

    /**
     * 關閉手機藍牙
     */
    public void disableBluetooth() {
        if (null != scanManager) {
            scanManager.disableBluetooth();
        }
    }

    /**
     * 打開手機藍牙
     */
    public void enableBluetooth() {
        if (null != scanManager) {
            scanManager.enableBluetooth();
        }
    }

    /**
     * 開始掃描藍牙
     */
    public void startScanBluetooth() {
        if (null != scanManager) {
            if (!scanManager.isScanning()) {
                scanManager.startScan();
            }
        }
    }

    /**
     * 停止掃描藍牙
     */
    public void stopScanBluetooth() {
        if (null != scanManager) {
            if (scanManager.isScanning()) {
                scanManager.stopScan();
            }
        }
    }

    /**
     * 斷開藍牙連接
     */
    public void disConnect() {
        if (null != okbleDevice) { // 如果當前已經連接其他設備,先斷開連接
            okbleDevice.removeDeviceListener(this);
            okbleDevice.disConnect(false);
            okbleDevice = null;
        }
    }

    /**
     * 發送資料給藍牙設備
     * @param data
     */
    public void writeData(byte[] data) {
        if (null != okbleDevice && null != writeCharacteristicModel && null != data && data.length > 0) {
            okbleDevice.addWriteOperation(writeCharacteristicModel.getUuid(), data, new OKBLEOperation.WriteOperationListener() {
                @Override
                public void onWriteValue(byte[] value) {
                    Log.e(tag, "藍牙寫資料成功");
                }

                @Override
                public void onFail(int code, String errMsg) {
                    Log.e(tag, "藍牙寫資料失敗:" + code + " " + errMsg);
                }

                @Override
                public void onExecuteSuccess(OKBLEOperation.OperationType type) {

                }
            });
        } else {
            Log.e(tag, "藍牙寫資料失敗: 藍牙沒有連接或沒發現可寫Characteristic");
        }
    }

    public void connectBluetoothDevice(BLEScanResult device) {
        // 先斷開當前連接
        disableBluetooth();
        okbleDevice = new OKBLEDeviceImp(context, device);
        okbleDevice.addDeviceListener(this);
        okbleDevice.connect(true);//true表示連接斷開后OKBLE的會自動重連
    }

    /**
     * 藍牙設備連接成功
     * @param deviceTAG
     */
    @Override
    public void onConnected(String deviceTAG) {
        Log.e(tag, "設備連接成功 " + deviceTAG);

        // 連上藍牙后,獲取藍牙的WriteCharacteristic用后面給藍牙設備發送資料,獲取藍牙的ReadCharacteristic用來監聽藍牙設備發送過來的資料
        List<OKBLEServiceModel> serviceModels = okbleDevice.getServiceModels();
        if (null != serviceModels && serviceModels.size() > 0) {
            for (int i = 0; i < serviceModels.size(); i++) {
                OKBLEServiceModel serviceModel = serviceModels.get(i);
                String serviceUUID = serviceModel.getUuid();
                if (serviceUUID.startsWith("aaaaaaa-")) { // 匹配找到讀寫的服務
                    List<OKBLECharacteristicModel> characteristicModels = serviceModel.getCharacteristicModels();
                    if (null != characteristicModels && characteristicModels.size() > 0) {
                        for (int j = 0; j < characteristicModels.size(); j++) {
                            OKBLECharacteristicModel characteristicModel = characteristicModels.get(j);
                            String characteristicUUID = characteristicModel.getUuid();
                            if (characteristicUUID.startsWith("bbbbbbbbb") && characteristicModel.isCanWrite() && characteristicModel.isCanWriteNoResponse()) { // 匹配找到寫的Characteristic
                                findWriteCharacteristic(characteristicModel);
                            } else if (characteristicUUID.startsWith("8653000b-") && characteristicModel.isCanNotify()) { // 匹配找到讀的Characteristic
                                findReadCharacteristic(characteristicModel);
                            }
                        }
                    }
                    break;
                }
            }
        }
    }

    @Override
    public void onDisconnected(String deviceTAG) {

    }

    @Override
    public void onReadBattery(String deviceTAG, int battery) {

    }

    /**
     * 接收到藍牙發送的資料
     * @param deviceTAG
     * @param uuid
     * @param value
     */
    @Override
    public void onReceivedValue(String deviceTAG, String uuid, byte[] value) {

    }

    @Override
    public void onWriteValue(String deviceTAG, String uuid, byte[] value, boolean success) {

    }

    @Override
    public void onReadValue(String deviceTAG, String uuid, byte[] value, boolean success) {

    }

    @Override
    public void onNotifyOrIndicateComplete(String deviceTAG, String uuid, boolean enable, boolean success) {

    }

    private void findWriteCharacteristic(OKBLECharacteristicModel characteristic) {
        if (null != okbleDevice && null != characteristic) {
            writeCharacteristicModel = characteristic;
        }
    }

    private void findReadCharacteristic(OKBLECharacteristicModel characteristic) {
        if (null != okbleDevice && null != characteristic) {
            String uuid = characteristic.getUuid();
            boolean enableNotifyEnable = okbleDevice.isNotifyEnabled(uuid);
            if (enableNotifyEnable) {
                Log.e(tag, "打開讀屬性成功");
            } else {
                okbleDevice.addNotifyOrIndicateOperation(uuid, true, new OKBLEOperation.NotifyOrIndicateOperationListener() {

                    @Override
                    public void onFail(int code, String errMsg) {
                        Log.e(tag, "打開讀屬性失敗");

                    }

                    @Override
                    public void onExecuteSuccess(OKBLEOperation.OperationType type) {
                        Log.e(tag, "打開讀屬性成功");

                    }

                    @Override
                    public void onNotifyOrIndicateComplete() {
                        Log.e(tag, "打開讀屬性成功");
                    }
                });

            }

        }
    }
}

藍牙廣播資料包決議

廣播包有兩種: 廣播包 (Advertising Data)和 回應包 (Scan Response),其中廣播包是每個設備必須廣播的,而回應包是可選的,
每個包都是 31 位元組,分為有效資料和無效資料兩部分,

有效資料部分 :包含若干個廣播資料單元,稱為 AD Structure ,

AD Structure 的組成是:
第一個位元組是長度值 Len ,表示接下來的 Len 個位元組是資料部分,
資料部分的第一個位元組表示資料的型別 AD Type ,剩下的 Len - 1 個位元組是真正的資料 AD data ,其中 AD type 非常關鍵,決定了 AD Data 的資料代表的是什么和怎么決議,
無效資料部分 :因為廣播包的長度必須是 31 個 byte,如果有效資料部 分不到 31 自己,剩下的就用 0 補全,這部分的資料是無效的,解釋的時候,忽略即可,在 Android 中,系統會把這兩個資料拼接在一起,回傳一個 62 位元組的陣列,

例如:

第一個 位元組代表廣播資料單元的長度 ,02 轉為10進制就是 2代表其資料長度為2 , 而資料單元的第一個位元組代表型別 ,
01 代表 代表物理連接功能為普通發現模式 06代表其資料類容
緊接著下一個資料單元:
0B代表資料長度為11 ,資料型別為 02 即Serviceuuid代表是非完整的16bit uuid, 所以緊接著的后10位就是其uuid,
接下來就是下一個資料單元
首位是13轉為二進制就是19,其長度就是19,型別就是09 ,代表設備名稱,30-》字符0,65代表字符e,61代表字符a,73代表字符s,79代表字符y,4E代表N,65代表e,57代表W,44代表D,43代表C,53代表S ,00 代表字符null,01代表字符soh(SOH是序始字符(Start Of Header),它表示標題的開始),56代表V ,31代表字符1,2E代表字符.,30代表字符0,44代表D所有其設備名稱就是0easyNewDCS V1.0D,
接下來的一個資料單元長度是5,廣播型別12 連接間隔范圍,有四個位元組,接下來資料長度是02,型別是0A代表信號強度 剩余都是00000都是補位的無效資料,

廣播資料型別:
(1)Flags: TYPE = 0x01,這個資料用來標識設備 LE 物理連接的功能,DATA 是 0 到多個位元組的 Flag 值,每個 bit 上用 0 或者 1 來表示是否為 True,如果有任何一個 bit 不為 0,并且廣播包是可連接的,就必須包含此資料,各 bit 的定義如下: bit 0: LE 有限發現模式 bit 1: LE 普通發現模式 bit 2: 不支持 BR/EDR bit 3: 對 Same Device Capable(Controller) 同時支持 BLE 和 BR/EDR bit 4: 對 Same Device Capable(Host) 同時支持 BLE 和 BR/EDR bit 5…7: 預留
(2)Service UUID: 廣播資料中一般都會把設備支持的 GATT Service 廣播出來,用來告訴外面本設備所支持的 Service,有三種型別的 UUID:16 bit, 32bit, 128 bit,廣播中,每種型別型別有有兩個類別:完整和非完整的,這樣就共有 6 種 AD Type,
非完整的 16 bit UUID 串列: TYPE = 0x02;
完整的 16 bit UUID 串列: TYPE = 0x03;
非完整的 32 bit UUID 串列: TYPE = 0x04;
完整的 32 bit UUID 串列: TYPE = 0x05;
非完整的 128 bit UUID 串列: TYPE = 0x06;
完整的 128 bit UUID 串列: TYPE = 0x07;
(3) Local Name: 設備名字,DATA 是名字的字串, Local Name 可以是設備的全名,也可以是設備名字的縮寫,其中縮寫必須是全名的前面的若干字符, 設備全名: TYPE = 0x08 設備簡稱: TYPE = 0x09
(4)TX Power Level: TYPE = 0x0A,表示設備發送廣播包的信號強度,DATA 部分是一個位元組,表示 -127 到 + 127 dBm,
(5) 帶外安全管理(Security Manager Out of Band):TYPE = 0x11,DATA 也是 Flag,每個 bit 表示一個功能: bit 0: OOB Flag,0 表示沒有 OOB 資料,1 表示有 bit 1: 支持 LE bit 2: 對 Same Device Capable(Host) 同時支持 BLE 和 BR/EDR bit 3: 地址型別,0 表示公開地址,1 表示隨機地址 ,
(6)外設(Slave)連接間隔范圍:TYPE = 0x12,資料中定義了 Slave 最大和最小連接間隔,資料包含 4 個位元組:
前 2 位元組:定義最小連接間隔,取值范圍:0x0006 ~ 0x0C80,而 0xFFFF 表示未定義; 后 2 位元組:定義最大連接間隔,同上,不過需要保證最大連接間隔大于或者等于最小連接間隔,
(7) 服務搜尋:外圍設備可以要請中心設備提供相應的 Service,其資料定義和前面的 Service UUID 類似:
16 bit UUID 串列: TYPE = 0x14
32 bit UUID 串列: TYPE = 0x??
128 bit UUID 串列: TYPE = 0x15
(8) Service Data: Service 對應的資料,
16 bit UUID Service: TYPE = 0x16, 前 2 位元組是 UUID,后面是 Service 的資料;
32 bit UUID Service: TYPE = 0x??, 前 4 位元組是 UUID,后面是 Service 的資料;
128 bit UUID Service: TYPE = 0x??, 前 16 位元組是 UUID,后面是 Service 的資料;
(9) 公開目標地址:TYPE = 0x17,表示希望這個廣播包被指定的目標設備處理,此設備系結了公開地址,DATA 是目標地址串列,每個地址 6 位元組,
(10) 隨機目標地址:TYPE = 0x18,定義和前一個類似,表示希望這個廣播包被指定的目標設備處理,此設備系結了隨機地址,DATA 是目標地址串列,每個地址 6 位元組,
(11) Appearance:TYPE = 0x19,DATA 是表示了設備的外觀,
(12) 廠商自定義資料: TYPE = 0xFF,廠商自定義的資料中,前兩個位元組表示廠商 ID,剩下的是廠商自己按照需求添加,里面的資料內容自己定義,

專案原始碼:https://codechina.csdn.net/android1/projectbasic
上篇:android專題-資料庫Room 目錄 下篇: Android專題-常用第三方框架

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/256866.html

標籤:其他

上一篇:使用JabRef+TexStudio管理文獻的方法

下一篇:手擼六足機器人(一)----------PCA9685[16路舵機控制]各暫存器詳解及控制原始碼示例[STM32實作]

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more