# MinewMWB02Kit 说明文档

本套 SDK 仅支持 Minew 公司出品的蓝牙设备。

通过 SDK 可以帮助开发者处理手机和蓝牙设备之间的一切工作,包括:扫描设备,广播数据、连接设备,向设备写入数据,从设备接收数据等。

目前 SDK 仅支持 佩戴式SOS按钮设备使用。

# 前期工作

整体框架:MWB02BleManager 为设备管理类,在 APP 运行时始终是单例。MWB02Model 是设备实例类,此套件会为每一个设备生成一个实例,在扫描和连接后都会使用,内部包含设备广播数据,在扫描期间该数据会随着设备不停广播而更新。

MWB02BleManager :设备管理类,可以扫描周围的设备,并且可以连接它们,校验它们等

MWB02Model :扫描时获取到的 4G 智能工牌设备实例,继承自 BaseBleDeviceEntity

# 导入到工程

  1. 开发环境

    SDK 最低支持 Android 5.0,对应 API Level 为 21 。在 module 的 build.gradle 中设置 minSdkVersion 为 21 或 21 以上 :

    android {
        defaultConfig {
            applicationId "com.xxx.xxx"
            minSdkVersion 21
        }
    }
    
    1
    2
    3
    4
    5
    6
  2. 将 jar 包添加到 module 的 libs 文件夹下,并在该 modulebuild.gradle 中添加如下语句(直接添加依赖):

    implementation files('libs/lib_ble_base.jar')
    implementation files('libs/lib_ble_mwb02.jar')
    implementation files('libs/lib_ble_nl.jar')
    api 'org.lucee:bcprov-jdk15on:1.52.0'
    api 'com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.16.0'
    
    1
    2
    3
    4
    5

或者右键该 jar 文件,选择 Add as Library ,添加到当前module。

添加 .so 库文件,App 目录下 build.gradle 添加如下配置:

   android {
       defaultConfig {
           ndk {
               abiFilters 'armeabi-v7a','arm64-v8a','x86','x86_64'
           }
       }
       sourceSets {
           main {
               jniLibs.srcDirs = ['libs']
           }
       }
   }
1
2
3
4
5
6
7
8
9
10
11
12
  1. AndroidManifest.xml需要以下权限,如果targetSdkVersion大于23,则需要做权限管理以获取权限:

        <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" tools:node="replace" />
        <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" tools:node="replace" />
        <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
        <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
        <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
        <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
        <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
        <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
        <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
        <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

# 使用

SDK 分为扫描、连接和读写三个阶段。

# Android 6.0 系统以上,进行 BLE 扫描时,需要先申请到蓝牙权限后并且打开定位开关才能进行。

# 开始扫描

Android6.0 系统以上,进行 BLE 扫描时,需要先申请到蓝牙权限后并且打开定位开关才能进行。

开启蓝牙扫描需要首先打开蓝牙,如果未打开蓝牙就去扫描,APP 会闪退。可通过 BLETool.checkBluetooth(this) 来判断蓝牙是否已经打开。如果没有打开,可以先打开蓝牙。

MWB02BleManager mBleManager = MWB02BleManager.getInstance();

switch (BLETool.checkBluetooth(this)){
    case BLE_NOT_SUPPORT:
        Toast.makeText(this, "Not Support BLE", Toast.LENGTH_SHORT).show();
        break;
    case BLUETOOTH_ON:
		//设置扫描时长5分钟,sdk默认扫描时长5分钟
		mBleManager.startScan(this, 5 * 60 * 1000, new OnScanDevicesResultListener<MWB02Model>() {
            @Override
            public void onScanResult(List<MWB02Model> list) {
            }

            @Override
            public void onStopScan(List<MWB02Model> list) {
            }
        });
        break;
    case BLUETOOTH_OFF:
        Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(enableIntent, 4);
        break;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

在扫描期间 APP 能够通过 sdk 获取到设备当前的一部分数据。如下所示通过 MWB02Model 获取设备数据,该数据保存在广播帧对象中。

SDK 提供了 BaseBleDeviceEntity 作为 MWB02Model 的基类,用于存储设备的公有数据,如下表所示:

名称 类型 说明
macAddress String 设备mac
name String 设备名称
rssi int 信号强度

BaseBleDeviceEntity 还保存了一个 BaseNanoLinkFrame,内部用于存储其在扫描期间获取到的设备广播数据帧,可通过如下方式取出:

MWB02Model module;
MWB02AdvFrame mwb02AdvFrame = (MWB02AdvFrame) module.getNanoLinkFrame();
if (mwb02AdvFrame != null) {
    //mac 地址
    String macAddress = mwb02AdvFrame.getMac();
    //deviceName 设备名称
    String deviceName = mwb02AdvFrame.getDeviceName();
    //firmwareVersion 版本号
    String firmwareVersion = mwb02AdvFrame.getFirmwareVersion();
    //battery 电量百分比,默认:Integer.MIN_VALUE 表示未获取到电量
    int battery = mwb02AdvFrame.getBattery();
    //motion 移动状态: 0 移动中  1 静止
    int motion = mwb02AdvFrame.getMotion();
    //SOS 模式 :0 设备未进入 SOS 模式 ,1 设备正处于 SOS 模式
    int modeSos = mwb02AdvFrame.getModeSos();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

佩戴式SOS按钮设备有 1 种广播帧类型。

  1. 设备帧

    • MWB02AdvFrame

      名称 类型 说明
      mac String 固件mac
      deviceName String 设备名称
      firmwareVersion String 版本号
      battery int 电量百分比,默认:Integer.MIN_VALUE 表示未获取到电量
      motion int 移动状态: 0 移动中 1 静止
      modeSos int SOS 模式 :0 设备未进入 SOS 模式 ,1 设备正处于 SOS 模式

# 连接

连接前一般需要先停止扫描 ,SDK 提供了连接和断开连接方法。

MWB02BleManager mBleManager = MWB02BleManager.getInstance();
//停止扫描
mBleManager.stopScan(context);
//连接 : module 为准备连接的设备 
MWB02Model module;
mBleManager.connect(context,module);
//断开连接 :macAddress 为设备 mac
mBleManager.disConnect(macAddress);
1
2
3
4
5
6
7
8

注意:连接设备前,请确认是否扫描到设备,如果没扫描到设备广播,调用连接方法,将连接失败。

在调用 connect() 后,SDK 中会对连接过程会有状态监听。

//设置监听器
mBleManager.setOnConnStateListener(new OnConnStateListener() {
    /*
     * 连接过程中的状态回调
     * @param macAddress      设备mac
     * @param BleConnectionState 状态
     */
    @Override
    public void onUpdateConnState(String address, BleConnectionState state) {
        switch (state) {
            case Connecting:
				//调用connect()后就会回调该状态
                break;
            case Connected:
                //初步连接成功,作为一个过渡阶段,此时并未真正成功
                break;
            case Bond_None:
				//设备配对回调,此处为无效配对状态
                break;
            case Bond_Bonding:
				//设备配对回调,此处为正在配对状态
                break;
            case Bond_Bonded:
				//设备配对回调,此处为配对完成状态
                //设备配对完成,才能通过 MWB02BleManager 中提供的方法操作设备
                break;
            case ConnectComplete:
				//设备连接完成,才能通过 MWB02BleManager 中提供的方法操作设备
                break;
            case Disconnect:
				//连接失败或者设备断开连接会回调,主动断开不会回调该状态
                break;
            default:
                break;
        }
    }
});
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

在连接过程中,sdk会返回多个连接状态到app中,app需要做好处理。

  • BleConnectionState.ConnectingBleConnectionState.Connected: 连接设备中,在这里状态下不要做耗时操作,因为此时在连接设备发现服务,并发送认证数据等。
  • BleConnectionState.Bond_None: 设备配对无效状态。
  • BleConnectionState.Bond_Bonding: 设备正在配对状态。
  • BleConnectionState.Bond_Bonded: 设备配对完成状态,才能通过 MWB02BleManager 中提供的方法操作设备。
  • BleConnectionState.ConnectComplete: 设备已经连接成功,可以进行读写操作,比如配置广播参数、读取历史数据等。
  • BleConnectionState.Disconnect: 连接失败或者设备断开连接会回调。

# 设备配置读取写入操作

设备配置读取写入操作 API 如下,使用 MWB02BleManager mBleManager = MWB02BleManager.getInstance() 对象调用完成:

    /**
     * 清空扫描设备列表
     */
     void clearScanResult();

    /**
     * 设置默认扫描时长
     *
     * @param scanTime 扫描时长,单位为 ms
     */
     void setDefaultScanTime(int scanTime);

    /**
     * 开始扫描设备
     *
     * @param context 上下文
     * @param listener 监听器
     */
     void startScan(Context context, OnScanDevicesResultListener listener) ;

    /**
     * 开始扫描设备
     *
     * @param context 上下文
     * @param scanTime 扫描时长,单位 ms
     * @param listener 监听器
     */
     void startScan(Context context, int scanTime, OnScanDevicesResultListener listener) ;

    /**
     * 停止扫描设备
     *
     * @param context 上下文
     */
     void stopScan(Context context) ;

    /**
     * 判断是否正在扫描
     *
     */
     boolean isScanning() ;

    /**
     * 恢复出厂设置
     *
     * @param macAddress 设备mac
     * @param listener 监听器
     */
     void reset(String macAddress, OnModifyConfigurationListener listener);

    /**
     * 关机
     *
     * @param macAddress 设备mac
     * @param listener 监听器
     */
     void powerOff(String macAddress, OnModifyConfigurationListener listener);

    /**
     * 重启
     *
     * @param macAddress 设备mac
     * @param listener 监听器
     */
     void reboot(String macAddress, OnModifyConfigurationListener listener);

    /**
     * 连接设备
     *
     * @param context 上下文
     * @param macAddress 设备mac
     */
	 void connect(Context context, String macAddress);

    /**
     * 连接设备
     *
     * @param context 上下文
     * @param module 设备实体
     */
	 void void connect(Context context, MWC03Model module);

    /**
     * 断开连接
     *
     * @param macAddress 设备mac
     */
	 void void disConnect(String macAddress);

    /**
     * 设置设备连接监听器
     *
     * @param listener 监听器
     */
	void setOnConnStateListener(OnConnStateListener listener)

    /**
     * 设备固件升级
     *
     * @param context  上下文对象
     * @param macAddress  设备mac
     * @param isLinkUpgrade :true url升级,false 固件包升级。默认值false,不支持url升级方式
     * @param filePath OTA文件路径,url升级时可为null
     * @param upgradeData OTA数据,url升级时可为null
     * @param fileUpgradeTarget 升级固件目标,OTA数据,默认 fileUpgradeTarget="main",链接升级方式无需修改,只有文件升级方式需要填入
     * @param linkUpgradeTarget 升级固件目标,OTA数据,默认 linkUpgradeTarget="app",文件升级方式无需修改,只有链接升级方式需要填入
     * @param listener 监听器
     */
     void firmwareUpgrade(Context context, String macAddress, boolean isLinkUpgrade, String filePath, byte[] upgradeData, String fileUpgradeTarget,String linkUpgradeTarget, OnFirmwareUpgradeListener listener) ;

    /**
     * 查询固件版本
     *
     * @param macAddress 设备mac
     * @param listener 监听器
     */
	void getFirmwareVersion(String macAddress, OnQueryResultListener<FirmwareVersionModel> listener);

    //关于 FirmwareVersionModel 类简要内容
	public class FirmwareVersionModel {
        List<VersionInfo> versionInfoList;
    }

    //关于 VersionInfo 类简要内容
	public class VersionInfo {
        // 固件名称
        private String firmwareName;
        // 固件类型
        private int firmwareType;
        // 固件版本
        private String firmwareVersion;
        // 固件插槽
        private String slot;
        // 固件类型对应的升级方式:img 支持镜像文件方式升级,hl 支持链接方式升级,file 支持文件方式升级
        private ArrayList<String> methods;
    }

	/**
     * 获取触发模式
     * @param macAddress  MAC 地址
     * @param listener 获取触发模式结果回调
     */
	void getTriggerMode(String macAddress, OnQueryResultListener<TriggerMode> listener);
	
	//关于 TriggerMode 类简要内容
	public class TriggerMode {
        //按下事件类型
        public static final String KEVT_PRESS = "10";
        //双击事件类型
    	public static final String KEVT_DOUBLE = "2";
        //事件类型,必须是 KEVT_PRESS 或者 KEVT_DOUBLE 的数值
        private String kevt;
        //此字段表示设备在激活后保持 Trigger 模式的持续时间。值 0 是一种特殊情况,表示该模式无限期地保持活动状态。单位为秒。
        private int dur;
        //退出事件类型,必须是 KEVT_PRESS 或者 KEVT_DOUBLE 的数值
        private String e_kevt;
    }

    /**
     * 设置触发模式
     * @param macAddress  MAC 地址
     * @param kevt 触发事件,必须是 KEVT_PRESS 或者 KEVT_DOUBLE 的数值
     * @param cont 触发持续时长,单位为秒,范围 1s ~ 300s。
     * @param listener 设置触发模式结果回调
     */
    void setTriggerMode(String macAddress, String kevt,int cont,String e_kevt, OnModifyConfigurationListener listener);

    /**
     * 触发状态重置
     * @param macAddress  MAC 地址
     * @param force 是否强制
     * @param isNeedAddForce 是否增加强制标记:true 表示上面的 force 设置有效,false 表示上面的 force 设置无效 
     * @param listener 触发状态重置结果监听器
     */
    void setStatusReset(String macAddress, boolean force, boolean isNeedAddForce,OnModifyConfigurationListener listener);

    /**
     * 获取广播参数
     * @param macAddress  MAC 地址
     * @param listener 获取广播参数结果监听器
     */
    void getAdvParameter(String macAddress, OnQueryResultListener<AdvParameter> listener);

	//关于 AdvParameter 类简要内容
	public class AdvParameter {
        //正常模式参数
    	private AdvParameterNormal norm;
		//触发模式参数——SOS模式
    	private AdvParameterTrigger trig;
    }

	//关于 AdvParameterNormal 类简要内容
	public class AdvParameterNormal {
		//该模式的开启关闭状态
    	private boolean en;
		//该模式的广播参数
    	private AdvParameterParam param;
    }

	//关于 AdvParameterTrigger 类简要内容
	public class AdvParameterTrigger {
		//该模式的开启关闭状态
    	private boolean en;
		//该模式的广播参数
    	private AdvParameterParam param;
    }

	//关于 AdvParameterParam 类简要内容
	public class AdvParameterParam {
		//该模式的广播间隔,100ms ~ 10000ms 可调,刻度为 100ms
    	private int intvl;
		//该模式的广播功率,有8个:-40,-20,-16,-12,-8,-4,0,4,8
    	private int txpwr;
    }

    /**
     * 配置广播参数
     * @param macAddress MAC 地址
     * @param advParameter 广播参数
     * @param listener 配置结果回调监听器
     */
    void setAdvParameter(String macAddress, AdvParameter advParameter, OnModifyConfigurationListener listener);

    /**
     * 获取扫描参数配置
     * @param macAddress  MAC 地址
     * @param listener 获取结果监听器
     */
    void getLeScanFilter(String macAddress, OnQueryResultListener<LeScanFilter> listener);

	//关于 LeScanFilter 类简要内容
    public class LeScanFilter {
		//扫描模式
        public LeScanFilterMode mode;
		//扫描过滤器,键只是 rssi,re_mac,re_raw 中的其中一个
        //当键为 : rssi 时,值:int 类型数据,范围 -100 ~ 0
        //当键为 : re_mac 时,值:String 类型数据,长度为 64 个字符的正则表达式
        //当键为 : re_raw 时,值:String 类型数据,长度为 192 个字符的正则表达式
        public List<TypeMap<String, Object>> filter;
    }

	//关于 LeScanFilterMode 类简要内容
    public class LeScanFilterMode {
		//正常模式
    	public LeScanFilterNorm norm;
		//触发模式 : SOS 或者 按键
    	public LeScanFilterTrig trig;
    }

	//关于 LeScanFilterNorm 类简要内容
    public class LeScanFilterNorm {
		//正常模式扫描开关状态
        public boolean en;
		//正常模式扫描参数
        public LeScanFilterParam param;
    }

	//关于 LeScanFilterTrig 类简要内容
    public class LeScanFilterTrig {
		//触发[SOS 或者 按键]模式扫描开关状态
        public boolean en;
		//触发[SOS 或者 按键]模式扫描参数
        public LeScanFilterParam param;
    }

	//关于 LeScanFilterParam 类简要内容
    public class LeScanFilterParam {
		//扫描间隔时长,100ms ~ 10000ms 可调,刻度为 100ms
        public float intvl;
		//扫描窗口时长,扫描窗口定义了每个间隔扫描的时间长度。单位为毫秒。此字段的有效范围为2.5ms至10240ms(10.24s)
        public float window;
		//扫描超时时长,LE扫描超时,PRD中单次扫描的持续时间。单位为毫秒。单位为毫秒。此字段的有效范围为500ms到655350ms(655.35s)
        public int timeout;
		//扫描重启时长,两次LE扫描之间的时间。单位为毫秒。
        public int gap;
		//是否为主动扫描:true 主动扫描 false 被动扫描
        public boolean active;
    }

    /**
     * 设置扫描参数
     * @param macAddress  MAC 地址
     * @param leScanFilter 扫描参数
     * @param listener 设置扫描参数结果监听器
     */
    void setLeScanFilter(String macAddress, LeScanFilter leScanFilter, OnModifyConfigurationListener listener);

    /**
     * 获取灯效配置
     * @param macAddress  MAC 地址
     * @param listener 获取灯效监听器
     */
    void getDeviceLEDConfig(@NonNull String macAddress, OnQueryResultListener<LedConfigEntity> listener);

	//关于 LedConfigEntity 类简要内容
    public class LedConfigEntity {
        public static final String COLOR_BLUE = "blue";
        public static final String COLOR_GREEN = "green";
        public static final String COLOR_RED = "red";
        public static final String COLOR_YELLOW = "yellow";
        public static final String COLOR_WHITE = "white";
        public static final String COLOR_MAGENTA = "magenta";
        public static final String COLOR_CYAN = "cyan";
        
        public static final String EFFECT_OFF = "off";
        public static final String EFFECT_STEADY = "steady";
        public static final String EFFECT_BLINK = "blink";
        
        //灯效颜色,灯效颜色必须是 COLOR_BLUE,COLOR_GREEN,COLOR_RED,COLOR_YELLOW,COLOR_WHITE,COLOR_MAGENTA,COLOR_CYAN 的其中一个
        private String color;
        //灯效效果,此字段指定LED效果,并且必须是以下字符串之一:EFFECT_OFF、EFFECT_STEADY、EFFECT_BLINK。
        private String effect;
        //灯效参数,定义了LED设置为闪烁时的详细行为:第一个参数表示LED在单个周期内保持点亮的持续时间(以毫秒为单位)范围 1 - 65535。第二个参数表示LED在同一周期内保持熄灭的持续时间(以毫秒为单位)范围 1 - 65535。第三个参数指定要执行的循环次数。
        private int[] params;
    }

    /**
     * 设置灯效
     * @param macAddress  MAC 地址
     * @param ledConfig 灯效配置参数
     * @param listener 设置灯效结果监听器
     */
    void setDeviceLEDConfig(@NonNull String macAddress, LedConfigEntity ledConfig, OnModifyConfigurationListener listener);

    /**
     * 获取 Relay Control 配置
     * @param macAddress  MAC 地址
     * @param listener 获取结果监听器
     */
    void getRelayControl(String macAddress, OnQueryResultListener<RelayControl> listener);

	//关于 RelayControl 类简要内容
    public class RelayControl {
		//此字段指定要中继的消息类型。它有两个不区分大小写的选项:“mac”和“imm”。“mac”表示中继内容是蓝牙LE mac地址;“imm”是指转播的内容是苹果iBeacon的major 和 minor 值。
        private String type;
		//此字段指定中继时必须保留多少字节的MAC地址。允许的长度为2、3、4和6。此字段是可选的,默认为2。只有当 type 字段设置为“mac”时,它才有效。如果获取数值为 Integer.MIN_VALUE 代表未获取到数据。
        private Integer aln = Integer.MIN_VALUE;
		//此字段指定一次要中继的数据项数量的上限(即每轮的最大设备数量)。中继时,设备始终选择最新的记录。例如,如果之前的蓝牙扫描产生了5条记录,并且限制设置为4条,则丢弃最旧的记录;如果限制设置为6,并且只找到5条有效记录,则中继5条记录。此字段是可选的,默认为5。如果获取数值为 Integer.MIN_VALUE 代表未获取到数据。范围为 1 - 100 。
        private Integer max = Integer.MIN_VALUE;
		//此字段指定中继尝试次数。这是可选的。如果不存在,则表示连续中继广告。如果获取数值为 Integer.MIN_VALUE 代表不存在。
        private Long cnt = Long.MIN_VALUE;
    }

    /**
     * 设置 Relay Control
     * @param macAddress  MAC 地址
     * @param relayControl Relay Control参数
     * @param listener 设置Relay Control结果监听器
     */
    void setRelayControl(String macAddress, RelayControl relayControl, OnModifyConfigurationListener listener);

    /**
     * 获取 Apple Ibeacon Frame 配置
     * @param macAddress  MAC 地址
     * @param listener 获取结果监听器
     */
    void getAppleIbeaconFrame(String macAddress, OnQueryResultListener<AppleIbeaconFrame> listener);

	//关于 AppleIbeaconFrame 类简要内容
    public class AppleIbeaconFrame {
		//正常模式参数
        public AppleIbeaconNorm norm;
    }

	//关于 AppleIbeaconNorm 类简要内容
    public class AppleIbeaconNorm {
		//ibeacon 帧广播的开启关闭状态
        public Boolean en;
		//ibeacon 帧广播数据
        public AppleIbeaconData data;
		//ibeacon 帧广播参数
        public AppleIbeaconParam param;
    }

	//关于 AppleIbeaconData 类简要内容
    public class AppleIbeaconData {
        //Apple iBeacon 广告帧中的 uuid,长度必须为 32
        private String uuid;
		//Apple iBeacon 广告帧中的 major,范围 0-65535
        private Integer major;
		//Apple iBeacon 广告帧中的 minor,范围 0-65535
        private Integer minor;
		//Apple iBeacon 广告帧中 1 米处的校准发射功率,单位为 dBm,范围 -128 - 127
        private Integer cpwr;
    }

	//关于 AppleIbeaconParam 类简要内容
    public class AppleIbeaconParam {
        //Apple iBeacon 广告帧中的广播间隔,100ms ~ 10000ms 可调,刻度为 100ms
        private Integer intvl;
		//Apple iBeacon 广告帧中的广播功率,有8个:-40,-20,-16,-12,-8,-4,0,4,8 单位dBm
        private Integer txpwr;
    }

    /**
     * 设置 Apple Ibeacon Frame
     * @param macAddress  MAC 地址
     * @param appleIbeaconFrame Apple Ibeacon Frame参数
     * @param listener 设置结果监听器
     */
    void setAppleIbeaconFrame(String macAddress, AppleIbeaconFrame appleIbeaconFrame, OnModifyConfigurationListener listener);

    /**
     * 获取 Eddystone Uid Frame 配置
     * @param macAddress  MAC 地址
     * @param listener 获取结果监听器
     */
    void getEddystoneUidFrame(String macAddress, OnQueryResultListener<EddystoneUidFrame> listener);

	//关于 EddystoneUidFrame 类简要内容
    public class EddystoneUidFrame {
		//正常模式参数
        public EddystoneUidNorm norm;
    }

	//关于 EddystoneUidNorm 类简要内容
    public class EddystoneUidNorm {
		//uid 帧广播的开启关闭状态
        private Boolean en;
		//uid 帧广播数据
        private EddystoneUidData data;
		//uid 帧广播参数
        private EddystoneUidParam param;
    }

	//关于 EddystoneUidData 类简要内容
    public class EddystoneUidData {
		//uid 广告帧中 0 米处的校准发射功率,单位为 dBm,范围 -100 ~ 20
        private Integer cpwr;
		//NameSpace ID 长度必须为 20
        private String nid;
		//Instance ID 长度必须为 12
        private String bid;
    }

	//关于 EddystoneUidParam 类简要内容
    public class EddystoneUidParam {
        //uid 广告帧中的广播间隔,100ms ~ 10000ms 可调,刻度为 100ms
        private Integer intvl;
		//uid 广告帧中的广播功率,有8个:-40,-20,-16,-12,-8,-4,0,4,8 单位dBm
        private Integer txpwr;
    }

    /**
     * 设置 EddystoneUidFrame参数
     * @param macAddress  MAC 地址
     * @param eddystoneUidFrame EddystoneUidFrame参数
     * @param listener 设置结果监听器
     */
    void setEddystoneUidFrame(String macAddress, EddystoneUidFrame eddystoneUidFrame, OnModifyConfigurationListener listener);

    /**
     * 获取 Eddystone Url Frame 配置
     * @param macAddress  MAC 地址
     * @param listener 获取结果监听器
     */
    void getEddystoneUrlFrame(String macAddress, OnQueryResultListener<EddystoneUrlFrame> listener);

	//关于 EddystoneUrlFrame 类简要内容
    public class EddystoneUrlFrame {
		//正常模式参数
        public EddystoneUrlNorm norm;
    }

	//关于 EddystoneUrlNorm 类简要内容
    public class EddystoneUrlNorm {
		//url 帧广播的开启关闭状态
        private Boolean en;
		//url 帧广播数据
        private EddystoneUrlData data;
		//url 帧广播参数
        private EddystoneUrlParam param;
    }

	//关于 EddystoneUrlData 类简要内容
    public class EddystoneUrlData {
		//url 广告帧中 0 米处的校准发射功率,单位为 dBm,范围 -100 ~ 20
        private Integer cpwr;
		//url 广告帧中的 url 数据
        private String url;
    }

	//关于 EddystoneUrlParam 类简要内容
    public class EddystoneUrlParam {
        //url 广告帧中的广播间隔,100ms ~ 10000ms 可调,刻度为 100ms
        private Integer intvl;
		//url 广告帧中的广播功率,有8个:-40,-20,-16,-12,-8,-4,0,4,8 单位dBm
        private Integer txpwr;
    }

    /**
     * 设置 EddystoneUrlFrame
     * @param macAddress  MAC 地址
     * @param eddystoneUrlFrame EddystoneUrlFrame参数
     * @param listener 设置结果监听器
     */
    void setEddystoneUrlFrame(String macAddress, EddystoneUrlFrame eddystoneUrlFrame, OnModifyConfigurationListener listener);


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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499

对部分方法进行补充说明:

  1. 给设备授予手机当前时间:

    	
    	// 首先要检查蓝牙开关是否打开,只有在打开的情况下才能开启手机的 CurrentTimeService 服务,如果蓝牙开关关闭去开启,部分 Android 手机系统可能会出错。并且需要监听蓝牙开关状态,当蓝牙开关开启时候需要开启服务 startSyncTimeServer,关闭的时候需要关闭服务 closeSyncTimeServer。注意,当 activity 页面 ondestroy 时,也需要关闭该服务 closeSyncTimeServer
    	MWB02BleManager mBleManager = MWB02BleManager.getInstance();
    	// 开启服务
    	mBleManager.startSyncTimeServer(context);
        // 关闭服务
        mBleManager.closeSyncTimeServer(context); 
    
    
    	// 因为部分手机会偶现授时失败的情况,所以需要判断授时是否成功,建议在设备连接成功 3 秒后判断
    	mBleManager.isDeviceReadSyncTime(context); 
    	// 如果授时操作不成功,那么需要主动给设备发送授时指令
    	mBleManager.writeTimeToDevice(macAddress, onWriteTimeToDeviceListener);
    
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
  2. 固件升级。

    
    /**
     * 固件升级
     *
     * @param macAddress  设备mac
     * @param upgradeData 升级包数据
     * @param listener    监听器
     */
    mBleManager.firmwareUpgrade(mac,false,0, upgradeData, new OnFirmwareUpgradeListener() {
        
        /**
         * 升级包数据写入进度
         */
        @Override
        public void updateProgress(int progress) {
    		
        }
    
        /**
         * 升级成功回调,此时设备会主动跟手机断开连接,所以会触发OnConnStateListener回调,返回
         * BleConnectionState.Disconnect状态
         */
        @Override
        public void upgradeSuccess() {
    
        }
        
        /**
         * 升级失败
         */
        @Override
        public void upgradeFailed() {
    
        }
    });
    
    
    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
  3. 查询历史记录

    
    // 因为历史记录读取是个耗时操作,所以必须在子线程进行调用,不能在主线程调用
    lifecycleScope.launch(Dispatchers.IO) {
        					//历史记录的查询者 StorageIndexRecordSearcher
        					//查询数据回调的泛型类型 StorageActualDataClick
        					//调用 getStorageIndexRecordsNeedSearchFile 进行查询,参数如下
                            StorageIndexRecordSearcher<StorageActualDataClick>().getStorageIndexRecordsNeedSearchFile(
                                //上下文对象
                                context,
                                //设备的mac地址
                                macAddress,
                                //是否开启查询历史记录的日志,false 为不开启,true 为开启
                                false,
                                //历史记录的查询条件
                                StorageIndexRecordCondition()
                                	//查询的数据类型
                                    .setStorageDataBlockType(StorageDataBlockType.Click)
                                	//查询的对应类型数据的实例,一般使用 StorageIndexInstance.INSTANCE_DEFAULT
                                    .setInstance(StorageIndexInstance.INSTANCE_DEFAULT)
                                	//查询历史数据的开始时间,queryAllData 该变量由开发者自己定义,true 为查询所有数据,false 为按时间段进行查询,该参数的单位为秒
                                    .setStartTime(when (queryAllData){
                                        true ->0
                                        else  ->selectedStartTimeDate.time/1000
                                    })
                                	//查询历史数据的结束时间,queryAllData 该变量由开发者自己定义,true 为查询所有数据,false 为按时间段进行查询,该参数的单位为秒
                                    .setEndTime(when (queryAllData){
                                        true ->System.currentTimeMillis()/1000
                                        else  ->selectedEndTimeDate.time/1000
                                    })
                            ) { 
                                //resultClickList StorageDataBlockType.Click类型历史数据的查询结果,如果结果为空,表示该时间段没有历史记录
                                //exceptionClick 查询如果数据如果发生了异常,会在此处给予开发者提示,如果没有异常,那么该值为 null
                                resultClickList,exceptionClick ->
                                LogUtil.e("exceptionClick  ${exceptionClick?:" exceptionClick null "}")
                                    
    							//查询数据回调的泛型类型 StorageActualDataScanResultAdvertisingRawData
                                StorageIndexRecordSearcher<StorageActualDataScanResultAdvertisingRawData>().getStorageIndexRecordsNeedSearchFile(
                                    //上下文对象
                                    context,
                                    //设备的mac地址
                                    macAddress,
                                    //是否开启查询历史记录的日志,false 为不开启,true 为开启
                                    false,
                                    //历史记录的查询条件
                                    StorageIndexRecordCondition()
                                    	//查询的数据类型
                                        .setStorageDataBlockType(StorageDataBlockType.Scan_Result)
                                    	//查询的对应类型数据的实例,一般使用 StorageIndexInstance.INSTANCE_DEFAULT
                                        .setInstance(StorageIndexInstance.INSTANCE_DEFAULT)
                                    	//查询历史数据的开始时间,queryAllData 该变量由开发者自己定义,true 为查询所有数据,false 为按时间段进行查询,该参数的单位为秒
                                        .setStartTime(when (queryAllData){
                                            true ->0
                                            else  ->selectedStartTimeDate.time/1000
                                        })
                                    	//查询历史数据的结束时间,queryAllData 该变量由开发者自己定义,true 为查询所有数据,false 为按时间段进行查询,该参数的单位为秒
                                        .setEndTime(when (queryAllData){
                                            true ->System.currentTimeMillis()/1000
                                            else  ->selectedEndTimeDate.time/1000
                                        })
                                    	//由于安卓手机系统对 App 内存限制要求,查询数据过多可能会导致内存溢出,所以需要限制查询历史数据的最大数量
                                        .setMaximumQuantityLimit(StorageIndexRecordCondition.QuantityLimit.LIMIT_10000)
                                ) { 
                                    //resultStorageActualDataScanResultAdvertisingRawDataList StorageDataBlockType.Scan_Result类型历史数据的查询结果,如果结果为空,表示该时间段没有历史记录
                                    //exceptionScanResult 查询如果数据如果发生了异常,会在此处给予开发者提示,如果没有异常,那么该值为 null
                                    resultStorageActualDataScanResultAdvertisingRawDataList,exceptionScanResult ->
                                    LogUtil.e("exceptionScanResult  ${exceptionScanResult?:" exceptionScanResult null "}")
                                    storageActualDataScanResultAdvertisingRawDataList = resultStorageActualDataScanResultAdvertisingRawDataList
                                    if (resultStorageActualDataScanResultAdvertisingRawDataList!=null){
                                        LogUtil.e("resultStorageActualDataScanResultAdvertisingRawDataList  ${resultStorageActualDataScanResultAdvertisingRawDataList!!.size}")
                                    }
                                    /**
                                    * 历史数据读取完成,需切换到主线程进行界面更新处理
                                    */
                                    lifecycleScope.launch (Dispatchers.Main){
                                        //主线程处理历史数据相关操作
                                    }
                                }
                            }
                        }
    
    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

# 文档更新记录

  • 2026/02/11 新增 MWB02 设备操作基本功能 API
上次更新:: 2026/2/11 10:36:25